mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 11:12:35 +02:00
Fixed backup permissions
This commit is contained in:
@@ -253,47 +253,61 @@ async def create_backup(
|
||||
|
||||
from backend.app.core.database import engine
|
||||
|
||||
base_dir = app_settings.base_dir
|
||||
db_path = Path(app_settings.database_url.replace("sqlite+aiosqlite:///", ""))
|
||||
try:
|
||||
base_dir = app_settings.base_dir
|
||||
db_path = Path(app_settings.database_url.replace("sqlite+aiosqlite:///", ""))
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_path = Path(temp_dir)
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_path = Path(temp_dir)
|
||||
|
||||
# 1. Checkpoint WAL to ensure all data is in main db file
|
||||
async with engine.begin() as conn:
|
||||
await conn.execute(text("PRAGMA wal_checkpoint(TRUNCATE)"))
|
||||
# 1. Checkpoint WAL to ensure all data is in main db file
|
||||
async with engine.begin() as conn:
|
||||
await conn.execute(text("PRAGMA wal_checkpoint(TRUNCATE)"))
|
||||
|
||||
# 2. Copy database file
|
||||
shutil.copy2(db_path, temp_path / "bambuddy.db")
|
||||
# 2. Copy database file
|
||||
shutil.copy2(db_path, temp_path / "bambuddy.db")
|
||||
|
||||
# 3. Copy data directories (if they exist)
|
||||
dirs_to_backup = [
|
||||
("archive", base_dir / "archive"),
|
||||
("virtual_printer", base_dir / "virtual_printer"),
|
||||
("plate_calibration", app_settings.plate_calibration_dir),
|
||||
("icons", base_dir / "icons"),
|
||||
("projects", base_dir / "projects"),
|
||||
]
|
||||
# 3. Copy data directories (if they exist)
|
||||
dirs_to_backup = [
|
||||
("archive", base_dir / "archive"),
|
||||
("virtual_printer", base_dir / "virtual_printer"),
|
||||
("plate_calibration", app_settings.plate_calibration_dir),
|
||||
("icons", base_dir / "icons"),
|
||||
("projects", base_dir / "projects"),
|
||||
]
|
||||
|
||||
for name, src_dir in dirs_to_backup:
|
||||
if src_dir.exists() and any(src_dir.iterdir()):
|
||||
shutil.copytree(src_dir, temp_path / name)
|
||||
for name, src_dir in dirs_to_backup:
|
||||
if src_dir.exists() and any(src_dir.iterdir()):
|
||||
try:
|
||||
shutil.copytree(src_dir, temp_path / name)
|
||||
except shutil.Error as e:
|
||||
# Some files may have restricted permissions (e.g., SSL keys)
|
||||
# Log the error but continue with partial backup
|
||||
logger.warning(f"Some files in {name} could not be copied: {e}")
|
||||
except PermissionError as e:
|
||||
logger.warning(f"Permission denied copying {name}: {e}")
|
||||
|
||||
# 4. Create ZIP
|
||||
zip_buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
for file_path in temp_path.rglob("*"):
|
||||
if file_path.is_file():
|
||||
arcname = file_path.relative_to(temp_path)
|
||||
zf.write(file_path, arcname)
|
||||
# 4. Create ZIP
|
||||
zip_buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
for file_path in temp_path.rglob("*"):
|
||||
if file_path.is_file():
|
||||
arcname = file_path.relative_to(temp_path)
|
||||
zf.write(file_path, arcname)
|
||||
|
||||
zip_buffer.seek(0)
|
||||
filename = f"bambuddy-backup-{datetime.now().strftime('%Y%m%d-%H%M%S')}.zip"
|
||||
zip_buffer.seek(0)
|
||||
filename = f"bambuddy-backup-{datetime.now().strftime('%Y%m%d-%H%M%S')}.zip"
|
||||
|
||||
return StreamingResponse(
|
||||
zip_buffer,
|
||||
media_type="application/zip",
|
||||
headers={"Content-Disposition": f"attachment; filename={filename}"},
|
||||
return StreamingResponse(
|
||||
zip_buffer,
|
||||
media_type="application/zip",
|
||||
headers={"Content-Disposition": f"attachment; filename={filename}"},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Backup failed: {e}", exc_info=True)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"success": False, "message": "Backup failed. Check server logs for details."},
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user