Spaces:
Running
Running
refactor: implement two-pass backup creation to automatically exclude frontend cache if tarball exceeds size limits
Browse files- postiz-sync.py +35 -17
- start.sh +1 -1
postiz-sync.py
CHANGED
|
@@ -149,37 +149,55 @@ def _exclude_next_cache(tarinfo: tarfile.TarInfo) -> tarfile.TarInfo | None:
|
|
| 149 |
return tarinfo
|
| 150 |
|
| 151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
def create_backup_tarball(dump_file: str) -> tuple[str | None, bool]:
|
| 153 |
temp_dir = tempfile.mkdtemp()
|
| 154 |
tarball = Path(temp_dir) / "huggingpost-backup.tar.gz"
|
| 155 |
try:
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
if UPLOADS_DIR.exists():
|
| 159 |
-
tar.add(str(UPLOADS_DIR), arcname="uploads")
|
| 160 |
-
if SECRETS_DIR.exists():
|
| 161 |
-
tar.add(str(SECRETS_DIR), arcname=".secrets")
|
| 162 |
-
# Include compiled frontend so subsequent restarts skip the 5-min build.
|
| 163 |
-
# Exclude .next/cache (webpack cache) β large and not needed to run.
|
| 164 |
-
if NEXT_DIR.exists() and (NEXT_DIR / "BUILD_ID").exists():
|
| 165 |
-
tar.add(str(NEXT_DIR), arcname="frontend-next", filter=_exclude_next_cache)
|
| 166 |
-
logger.debug("Included .next in tarball (webpack cache excluded)")
|
| 167 |
size = tarball.stat().st_size
|
| 168 |
size_mb = size / 1024 / 1024
|
| 169 |
logger.debug(f"Tarball created ({size_mb:.2f} MB)")
|
| 170 |
if size > SYNC_MAX_FILE_BYTES:
|
| 171 |
-
logger.
|
| 172 |
-
f"
|
| 173 |
-
"
|
| 174 |
-
"
|
| 175 |
)
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
return str(tarball), True
|
| 178 |
except Exception as e:
|
| 179 |
logger.error(f"Failed to create tarball: {e}")
|
| 180 |
return None, False
|
| 181 |
|
| 182 |
|
|
|
|
| 183 |
def upload_to_hf(backup_file: str) -> bool:
|
| 184 |
if not HF_TOKEN:
|
| 185 |
logger.warning("HF_TOKEN not set β skipping upload")
|
|
@@ -348,7 +366,7 @@ def cmd_sync() -> bool:
|
|
| 348 |
write_status(status); return False
|
| 349 |
tarball, ok = create_backup_tarball(dump)
|
| 350 |
if not ok:
|
| 351 |
-
status.update({"last_error": "tarball creation failed", "db_status": "error"})
|
| 352 |
write_status(status); return False
|
| 353 |
ok = upload_to_hf(tarball)
|
| 354 |
status["last_sync_time"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
|
|
| 149 |
return tarinfo
|
| 150 |
|
| 151 |
|
| 152 |
+
def _write_tarball(tarball: Path, dump_file: str, include_next: bool) -> None:
|
| 153 |
+
"""Write the backup tarball. Raises on any error."""
|
| 154 |
+
with tarfile.open(tarball, "w:gz") as tar:
|
| 155 |
+
tar.add(dump_file, arcname="postiz.sql")
|
| 156 |
+
if UPLOADS_DIR.exists():
|
| 157 |
+
tar.add(str(UPLOADS_DIR), arcname="uploads")
|
| 158 |
+
if SECRETS_DIR.exists():
|
| 159 |
+
tar.add(str(SECRETS_DIR), arcname=".secrets")
|
| 160 |
+
if include_next and NEXT_DIR.exists() and (NEXT_DIR / "BUILD_ID").exists():
|
| 161 |
+
tar.add(str(NEXT_DIR), arcname="frontend-next", filter=_exclude_next_cache)
|
| 162 |
+
logger.debug("Included .next in tarball (webpack cache excluded)")
|
| 163 |
+
|
| 164 |
+
|
| 165 |
def create_backup_tarball(dump_file: str) -> tuple[str | None, bool]:
|
| 166 |
temp_dir = tempfile.mkdtemp()
|
| 167 |
tarball = Path(temp_dir) / "huggingpost-backup.tar.gz"
|
| 168 |
try:
|
| 169 |
+
# First attempt: include compiled .next so subsequent restarts skip rebuild.
|
| 170 |
+
_write_tarball(tarball, dump_file, include_next=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
size = tarball.stat().st_size
|
| 172 |
size_mb = size / 1024 / 1024
|
| 173 |
logger.debug(f"Tarball created ({size_mb:.2f} MB)")
|
| 174 |
if size > SYNC_MAX_FILE_BYTES:
|
| 175 |
+
logger.warning(
|
| 176 |
+
f"Tarball with .next too large ({size_mb:.0f} MB > "
|
| 177 |
+
f"{SYNC_MAX_FILE_BYTES/1024/1024:.0f} MB limit) β "
|
| 178 |
+
"retrying without compiled frontend..."
|
| 179 |
)
|
| 180 |
+
# Second attempt: skip .next, keep essential DB + uploads + secrets.
|
| 181 |
+
tarball.unlink(missing_ok=True)
|
| 182 |
+
_write_tarball(tarball, dump_file, include_next=False)
|
| 183 |
+
size = tarball.stat().st_size
|
| 184 |
+
size_mb = size / 1024 / 1024
|
| 185 |
+
logger.debug(f"Tarball without .next: {size_mb:.2f} MB")
|
| 186 |
+
if size > SYNC_MAX_FILE_BYTES:
|
| 187 |
+
logger.error(
|
| 188 |
+
f"Backup still too large without .next ({size_mb:.0f} MB > "
|
| 189 |
+
f"{SYNC_MAX_FILE_BYTES/1024/1024:.0f} MB). "
|
| 190 |
+
"Move uploads to Cloudflare R2 (STORAGE_PROVIDER=cloudflare) "
|
| 191 |
+
"or raise SYNC_MAX_FILE_BYTES."
|
| 192 |
+
)
|
| 193 |
+
return None, False
|
| 194 |
return str(tarball), True
|
| 195 |
except Exception as e:
|
| 196 |
logger.error(f"Failed to create tarball: {e}")
|
| 197 |
return None, False
|
| 198 |
|
| 199 |
|
| 200 |
+
|
| 201 |
def upload_to_hf(backup_file: str) -> bool:
|
| 202 |
if not HF_TOKEN:
|
| 203 |
logger.warning("HF_TOKEN not set β skipping upload")
|
|
|
|
| 366 |
write_status(status); return False
|
| 367 |
tarball, ok = create_backup_tarball(dump)
|
| 368 |
if not ok:
|
| 369 |
+
status.update({"last_error": "tarball creation failed β backup too large or I/O error (check logs)", "db_status": "error"})
|
| 370 |
write_status(status); return False
|
| 371 |
ok = upload_to_hf(tarball)
|
| 372 |
status["last_sync_time"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
start.sh
CHANGED
|
@@ -87,7 +87,7 @@ export NOT_SECURED="${NOT_SECURED:-true}"
|
|
| 87 |
|
| 88 |
# Sync config
|
| 89 |
export SYNC_INTERVAL="${SYNC_INTERVAL:-300}"
|
| 90 |
-
export SYNC_MAX_FILE_BYTES="${SYNC_MAX_FILE_BYTES:-
|
| 91 |
export BACKUP_DATASET_NAME="${BACKUP_DATASET_NAME:-huggingpost-backup}"
|
| 92 |
|
| 93 |
# ββ Banner βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 87 |
|
| 88 |
# Sync config
|
| 89 |
export SYNC_INTERVAL="${SYNC_INTERVAL:-300}"
|
| 90 |
+
export SYNC_MAX_FILE_BYTES="${SYNC_MAX_FILE_BYTES:-524288000}" # 500 MB (default; covers .next + DB + uploads)
|
| 91 |
export BACKUP_DATASET_NAME="${BACKUP_DATASET_NAME:-huggingpost-backup}"
|
| 92 |
|
| 93 |
# ββ Banner βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|