Create a backup archive containing database dump and data directories. Returns the path to the created backup file.
()
| 227 | |
| 228 | |
| 229 | def create_backup() -> Path: |
| 230 | """ |
| 231 | Create a backup archive containing database dump and data directories. |
| 232 | Returns the path to the created backup file. |
| 233 | """ |
| 234 | backup_dir = get_backup_dir() |
| 235 | |
| 236 | # Use system timezone for filename (user-friendly), but keep internal timestamps as UTC |
| 237 | system_tz_name = CoreSettings.get_system_time_zone() |
| 238 | try: |
| 239 | system_tz = pytz.timezone(system_tz_name) |
| 240 | now_local = datetime.datetime.now(datetime.UTC).astimezone(system_tz) |
| 241 | timestamp = now_local.strftime("%Y.%m.%d.%H.%M.%S") |
| 242 | except Exception as e: |
| 243 | logger.warning(f"Failed to use system timezone {system_tz_name}: {e}, falling back to UTC") |
| 244 | timestamp = datetime.datetime.now(datetime.UTC).strftime("%Y.%m.%d.%H.%M.%S") |
| 245 | |
| 246 | backup_name = f"dispatcharr-backup-{timestamp}.zip" |
| 247 | backup_file = backup_dir / backup_name |
| 248 | |
| 249 | logger.info(f"Creating backup: {backup_name}") |
| 250 | |
| 251 | with tempfile.TemporaryDirectory(prefix="dispatcharr-backup-") as temp_dir: |
| 252 | temp_path = Path(temp_dir) |
| 253 | |
| 254 | # Determine database type and dump accordingly |
| 255 | if _is_postgresql(): |
| 256 | db_dump_file = temp_path / "database.dump" |
| 257 | _dump_postgresql(db_dump_file) |
| 258 | db_type = "postgresql" |
| 259 | else: |
| 260 | db_dump_file = temp_path / "database.sqlite3" |
| 261 | _dump_sqlite(db_dump_file) |
| 262 | db_type = "sqlite" |
| 263 | |
| 264 | # Create ZIP archive with compression and ZIP64 support for large files |
| 265 | with ZipFile(backup_file, "w", compression=ZIP_DEFLATED, allowZip64=True) as zip_file: |
| 266 | # Add database dump |
| 267 | zip_file.write(db_dump_file, db_dump_file.name) |
| 268 | |
| 269 | # Add metadata |
| 270 | metadata = { |
| 271 | "format": "dispatcharr-backup", |
| 272 | "version": 2, |
| 273 | "database_type": db_type, |
| 274 | "database_file": db_dump_file.name, |
| 275 | "created_at": datetime.datetime.now(datetime.UTC).isoformat(), |
| 276 | } |
| 277 | zip_file.writestr("metadata.json", json.dumps(metadata, indent=2)) |
| 278 | |
| 279 | logger.info(f"Backup created successfully: {backup_file}") |
| 280 | return backup_file |
| 281 | |
| 282 | |
| 283 | def restore_backup(backup_file: Path) -> None: |
nothing calls this directly
no test coverage detected