1212import tempfile
1313import zipfile
1414from pathlib import Path
15- from typing import Optional
1615from urllib .parse import urlparse
1716
1817from module .network .request_url import RequestURL
2524# 允许的下载域名白名单
2625ALLOWED_DOMAINS = ["github.com" , "codeload.github.com" ] # GitHub 的文件下载域名
2726
28- # 最大下载文件大小 (100MB )
29- MAX_DOWNLOAD_SIZE = 100 * 1024 * 1024
27+ # 最大下载文件大小 (10MB )
28+ MAX_DOWNLOAD_SIZE = 10 * 1024 * 1024
3029
3130
3231class DockerUpdater :
3332 """Docker 环境更新器"""
3433
3534 def __init__ (self ):
36- self .temp_dir : Optional [ Path ] = None
37- self .backup_dir = Path ("/app.bak " )
38- self .app_dir = Path ("/app" )
35+ self .temp_dir : Path | None = None
36+ self .backup_dir : Path = Path ("/app/backup " )
37+ self .app_dir : Path = Path ("/app" )
3938
4039 def _is_url_allowed (self , url : str ) -> bool :
4140 """检查 URL 是否在允许的白名单中
@@ -114,23 +113,23 @@ async def _download_file(self, url: str) -> Path:
114113 # 设置用户代理
115114 client .header ["User-Agent" ] = "Auto_Bangumi Docker Updater"
116115
117- response = await client .get_url (url , stream = True )
116+ response = await client .get_url (url )
118117
119118 # 检查文件大小
120119 content_length = response .headers .get ("content-length" )
121120 if content_length and int (content_length ) > MAX_DOWNLOAD_SIZE :
122121 raise Exception (f"File too large: { content_length } bytes" )
123122
124- # 下载文件
125- downloaded_size = 0
123+ # 直接获取文件内容
124+ content = response .content
125+ if len (content ) > MAX_DOWNLOAD_SIZE :
126+ raise Exception ("Downloaded file exceeds size limit" )
127+
128+ # 写入文件
126129 with open (download_path , "wb" ) as f :
127- async for chunk in response .aiter_bytes (8192 ):
128- downloaded_size += len (chunk )
129- if downloaded_size > MAX_DOWNLOAD_SIZE :
130- raise Exception ("Downloaded file exceeds size limit" )
131- f .write (chunk )
130+ f .write (content )
132131
133- logger .info (f"[DockerUpdater] Downloaded { downloaded_size } bytes to { download_path } " )
132+ logger .info (f"[DockerUpdater] Downloaded { len ( content ) } bytes to { download_path } " )
134133 return download_path
135134
136135 except Exception as e :
@@ -163,6 +162,7 @@ def _extract_zip(self, zip_path: Path) -> Path:
163162
164163 # 找到实际的程序目录(通常是解压后的第一个子目录)
165164 extracted_items = list (extract_path .iterdir ())
165+ logger .debug (f"[DockerUpdater] Extracted items: { extracted_items } " )
166166 if len (extracted_items ) == 1 and extracted_items [0 ].is_dir ():
167167 return extracted_items [0 ]
168168 else :
@@ -173,61 +173,61 @@ def _extract_zip(self, zip_path: Path) -> Path:
173173 raise
174174
175175 def _backup_current_app (self ):
176- """备份当前应用 """
176+ """备份当前应用的 src 和 dist 目录 """
177177 try :
178178 # 删除旧的备份
179179 if self .backup_dir .exists ():
180180 shutil .rmtree (self .backup_dir )
181181
182- # 创建新备份
183- shutil .move (str (self .app_dir ), str (self .backup_dir ))
184- logger .info (f"[DockerUpdater] Backed up current app to { self .backup_dir } " )
182+ # 创建备份目录
183+ self .backup_dir .mkdir (exist_ok = True )
184+
185+ # 只备份 src 和 dist 目录
186+ src_dir = self .app_dir / "src"
187+ dist_dir = self .app_dir / "dist"
188+
189+ if src_dir .exists ():
190+ shutil .copytree (src_dir , self .backup_dir / "src" )
191+ logger .info (f"[DockerUpdater] Backed up src directory to { self .backup_dir / 'src' } " )
192+
193+ if dist_dir .exists ():
194+ shutil .copytree (dist_dir , self .backup_dir / "dist" )
195+ logger .info (f"[DockerUpdater] Backed up dist directory to { self .backup_dir / 'dist' } " )
185196
186197 except Exception as e :
187198 logger .error (f"[DockerUpdater] Backup failed: { e } " )
188199 raise
189200
190201 def _install_new_app (self , source_dir : Path ):
191- """安装新应用
202+ """安装新应用的 src 和 dist 目录
192203
193204 Args:
194205 source_dir: 新应用源目录
195206 """
196207 try :
197- # 移动新应用到目标位置
198- shutil .move (str (source_dir ), str (self .app_dir ))
199- logger .info (f"[DockerUpdater] Installed new app from { source_dir } " )
208+ # 更新 src 目录
209+ new_src = source_dir / "src"
210+ if new_src .exists ():
211+ target_src = self .app_dir / "src"
212+ if target_src .exists ():
213+ shutil .rmtree (target_src )
214+ shutil .copytree (new_src , target_src )
215+ logger .info (f"[DockerUpdater] Installed new src directory from { new_src } " )
216+
217+ # 更新 dist 目录
218+ new_dist = source_dir / "dist"
219+ if new_dist .exists ():
220+ target_dist = self .app_dir / "dist"
221+ if target_dist .exists ():
222+ shutil .rmtree (target_dist )
223+ shutil .copytree (new_dist , target_dist )
224+ logger .info (f"[DockerUpdater] Installed new dist directory from { new_dist } " )
200225
201226 except Exception as e :
202227 logger .error (f"[DockerUpdater] Installation failed: { e } " )
203228 raise
204229
205- def _restore_user_data (self ):
206- """恢复用户配置和数据"""
207- try :
208- # 恢复 config 目录
209- backup_config = self .backup_dir / "config"
210- new_config = self .app_dir / "config"
211-
212- if backup_config .exists ():
213- if new_config .exists ():
214- shutil .rmtree (new_config )
215- shutil .copytree (backup_config , new_config )
216- logger .info ("[DockerUpdater] Restored config directory" )
217-
218- # 恢复 data 目录
219- backup_data = self .backup_dir / "data"
220- new_data = self .app_dir / "data"
221-
222- if backup_data .exists ():
223- if new_data .exists ():
224- shutil .rmtree (new_data )
225- shutil .copytree (backup_data , new_data )
226- logger .info ("[DockerUpdater] Restored data directory" )
227-
228- except Exception as e :
229- logger .error (f"[DockerUpdater] Failed to restore user data: { e } " )
230- raise
230+
231231
232232 def _fix_permissions (self ):
233233 """修复文件权限"""
@@ -242,13 +242,29 @@ def _fix_permissions(self):
242242 def _rollback (self ):
243243 """回滚到备份版本"""
244244 try :
245- if self .backup_dir .exists ():
246- if self .app_dir .exists ():
247- shutil .rmtree (self .app_dir )
248- shutil .move (str (self .backup_dir ), str (self .app_dir ))
249- logger .info ("[DockerUpdater] Rolled back to backup version" )
250- else :
245+ if not self .backup_dir .exists ():
251246 logger .error ("[DockerUpdater] No backup found for rollback" )
247+ return
248+
249+ # 回滚 src 目录
250+ backup_src = self .backup_dir / "src"
251+ if backup_src .exists ():
252+ target_src = self .app_dir / "src"
253+ if target_src .exists ():
254+ shutil .rmtree (target_src )
255+ shutil .copytree (backup_src , target_src )
256+ logger .info ("[DockerUpdater] Rolled back src directory" )
257+
258+ # 回滚 dist 目录
259+ backup_dist = self .backup_dir / "dist"
260+ if backup_dist .exists ():
261+ target_dist = self .app_dir / "dist"
262+ if target_dist .exists ():
263+ shutil .rmtree (target_dist )
264+ shutil .copytree (backup_dist , target_dist )
265+ logger .info ("[DockerUpdater] Rolled back dist directory" )
266+
267+ logger .info ("[DockerUpdater] Rollback completed" )
252268
253269 except Exception as e :
254270 logger .error (f"[DockerUpdater] Rollback failed: { e } " )
@@ -278,7 +294,6 @@ async def update(self, download_url: str) -> dict:
278294 # 创建更新锁
279295 if not self ._create_update_lock ():
280296 raise Exception ("Another update is already in progress" )
281-
282297 try :
283298 logger .info ("[DockerUpdater] Starting Docker update process" )
284299
@@ -294,9 +309,6 @@ async def update(self, download_url: str) -> dict:
294309 # 4. 安装新应用
295310 self ._install_new_app (source_dir )
296311
297- # 5. 恢复用户数据
298- self ._restore_user_data ()
299-
300312 # 6. 修复权限
301313 self ._fix_permissions ()
302314
@@ -325,9 +337,9 @@ def force_restart(self):
325337 logger .info ("[DockerUpdater] Forcing container restart" )
326338 # 在 Docker 环境中,.sh 有监控进程会自动重启
327339 import sys
340+
328341 sys .exit (0 )
329342
330343
331344# 全局实例
332345docker_updater = DockerUpdater ()
333-
0 commit comments