File size: 5,359 Bytes
1451aee 9e713da 1451aee 9e713da 1451aee 9e713da 1451aee 9e713da 1451aee 9e713da 1451aee 9e713da 1451aee 9e713da 1451aee 9e713da 1451aee 5fd8a3c 9e713da 5fd8a3c 9e713da 5fd8a3c 9e713da 1451aee 5fd8a3c 1451aee 5fd8a3c 1451aee | 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 | import os
import sys
import tarfile
import shutil
import boto3
from botocore.client import Config
from flask import Flask, jsonify
from datetime import datetime
# Cloudflare R2 Config
R2_ACCOUNT_ID = os.getenv('R2_ACCOUNT_ID')
R2_ACCESS_KEY = os.getenv('R2_ACCESS_KEY')
R2_SECRET_KEY = os.getenv('R2_SECRET_KEY')
R2_BUCKET_NAME = os.getenv('R2_BUCKET_NAME')
STORAGE_DIR = 'storage'
PREFIX = 'apexkit_backup_'
MAX_BACKUPS_TO_KEEP = 5
def get_s3_client():
if not all([R2_ACCOUNT_ID, R2_ACCESS_KEY, R2_SECRET_KEY, R2_BUCKET_NAME]):
print("⚠️ R2 credentials missing in Hugging Face secrets. Skipping cloud storage operations.")
return None
endpoint_url = f"https://{R2_ACCOUNT_ID}.r2.cloudflarestorage.com"
return boto3.client('s3',
endpoint_url=endpoint_url,
aws_access_key_id=R2_ACCESS_KEY,
aws_secret_access_key=R2_SECRET_KEY,
config=Config(signature_version='s3v4')
)
def make_tarfile(output_filename, source_dir):
with tarfile.open(output_filename, "w:gz") as tar:
tar.add(source_dir, arcname=os.path.basename(source_dir))
def extract_tarfile(input_filename):
with tarfile.open(input_filename, "r:gz") as tar:
tar.extractall(path=".")
def clean_junk_folders():
"""Removes tmp and internal backup files before zipping to save massive amounts of space"""
for folder in ['storage/tmp', 'storage/backups']:
if os.path.exists(folder):
shutil.rmtree(folder, ignore_errors=True)
os.makedirs(folder, exist_ok=True)
def perform_backup():
s3 = get_s3_client()
if not s3:
return False, "S3 client not configured."
clean_junk_folders()
# Create a unique timestamped filename to bust Cloudflare's cache
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
archive_name = f"{PREFIX}{timestamp}.tar.gz"
print(f"📦 Compressing {STORAGE_DIR} to {archive_name}...")
make_tarfile(archive_name, STORAGE_DIR)
print(f"☁️ Uploading {archive_name} to R2 bucket '{R2_BUCKET_NAME}'...")
try:
s3.upload_file(archive_name, R2_BUCKET_NAME, archive_name)
print(f"✅ Upload successful! Size: {os.path.getsize(archive_name) / (1024*1024):.2f} MB")
# Clean up the local tarball so it doesn't waste disk space
os.remove(archive_name)
# GET RID OF THE OLD ONES
response = s3.list_objects_v2(Bucket=R2_BUCKET_NAME, Prefix=PREFIX)
if 'Contents' in response:
# Sort backups by date (newest first)
backups = sorted(response['Contents'], key=lambda x: x['LastModified'], reverse=True)
# If we have more than the max allowed, delete the oldest ones
if len(backups) > MAX_BACKUPS_TO_KEEP:
for old_backup in backups[MAX_BACKUPS_TO_KEEP:]:
old_key = old_backup['Key']
print(f"🗑️ Deleting old backup to save space: {old_key}")
s3.delete_object(Bucket=R2_BUCKET_NAME, Key=old_key)
return True, f"Backup successful: {archive_name}"
except Exception as e:
print(f"❌ Backup failed: {e}")
return False, str(e)
def perform_restore():
s3 = get_s3_client()
if not s3:
return False
print(f"☁️ Scanning for latest backup in R2 bucket '{R2_BUCKET_NAME}'...")
try:
# 1. Search for NEW timestamped backups
response = s3.list_objects_v2(Bucket=R2_BUCKET_NAME, Prefix=PREFIX)
latest_backup_key = None
if 'Contents' in response and len(response['Contents']) > 0:
# Sort by date and grab the absolute newest one
backups = sorted(response['Contents'], key=lambda x: x['LastModified'], reverse=True)
latest_backup_key = backups[0]['Key']
else:
# 2. LEGACY FALLBACK: If no new backups exist, look for the old one!
print("⚠️ No timestamped backups found. Attempting to restore legacy 'apexkit_storage_backup.tar.gz'...")
latest_backup_key = 'apexkit_storage_backup.tar.gz'
print(f"📥 Downloading backup: {latest_backup_key}...")
temp_download_name = "restore.tar.gz"
# Download the file
s3.download_file(R2_BUCKET_NAME, latest_backup_key, temp_download_name)
print(f"📦 Extracting {latest_backup_key}...")
extract_tarfile(temp_download_name)
# Clean up downloaded tarball
os.remove(temp_download_name)
print("✅ Restore successful! Files are fully up to date.")
return True
except Exception as e:
print(f"⚠️ Restore failed (Is the bucket empty?). Error: {e}")
return False
# --- LOCAL API SERVER ---
app = Flask(__name__)
@app.route('/backup', methods=['POST', 'GET'])
def trigger_backup():
success, msg = perform_backup()
if success:
return jsonify({"status": "success", "message": msg}), 200
else:
return jsonify({"status": "error", "message": msg}), 500
if __name__ == '__main__':
if len(sys.argv) > 1:
if sys.argv[1] == '--restore':
perform_restore()
elif sys.argv[1] == '--serve':
app.run(host='127.0.0.1', port=5000)
else:
print("Use --restore or --serve") |