Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
APP_NAME="Nova System Admin"

API_URL=
API_KEY=

TEST_BACKUP_PATH=
49 changes: 48 additions & 1 deletion api/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,57 @@
API_KEY
)

def check_schedule():
headers = {
"Authorization": f"Bearer {API_KEY}",
"Accept": "application/json"
}

response = requests.get(
f"{API_URL}/backups/check-schedule",
headers=headers
)

if response.status_code == 404:
return []

response.raise_for_status()
data = response.json()

if isinstance(data, dict):
jobs = data.get("data")
if jobs is None:
return []
if isinstance(jobs, list):
return jobs
if isinstance(jobs, dict):
return [jobs]

return []

def store_job_report(job_id, result):
headers = {
"Authorization": f"Bearer {API_KEY}",
"Accept": "application/json",
"Content-Type": "application/json"
}

payload = result.__dict__ if hasattr(result, "__dict__") else result

response = requests.post(
f"{API_URL}/backups/jobs/{job_id}/report",
headers=headers,
json=payload
)

response.raise_for_status()

return response.json()

def send_backup(result):
headers = {
"Authorization": f"Bearer {API_KEY}",
"Accept": "application/json"
"Accept": "application/json"
}

response = requests.post(
Expand Down
17 changes: 14 additions & 3 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
from config import TEST_BACKUP_PATH
from backup.backup import run_backup
from api.report import check_schedule
from backup.backup import run_backup_job

run_backup(TEST_BACKUP_PATH)
def main():
jobs = check_schedule()

if not jobs:
print("No backup schedules are due.")
return

for job in jobs:
run_backup_job(job)

if __name__ == "__main__":
main()
72 changes: 66 additions & 6 deletions backup/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,77 @@
from backup.zip import create_zip
from models.backup_result import BackupResult
from utils.checksum import sha256
from api.report import send_backup
from api.report import send_backup, store_job_report
from config import BACKUP_OUTPUT

def run_backup(source_path: str):
def run_backup_job(job: dict):
job_id = job["id"]
source_path = job["source_path"]
dest_dir = job.get("destination_path") or BACKUP_OUTPUT
expected_filename = job.get("expected_filename")

started = datetime.now()

try:
if expected_filename:
filename = expected_filename[:-4] if expected_filename.endswith(".zip") else expected_filename
else:
filename = f"backup-{started.strftime('%Y%m%d-%H%M%S')}"

destination = str(Path(dest_dir) / filename)
zip_file = create_zip(source_path, destination)
completed = datetime.now()
duration = int((completed - started).total_seconds())

result = BackupResult(
file_name=Path(zip_file).name,
file_path=zip_file,
file_size=Path(zip_file).stat().st_size,
checksum=sha256(zip_file),
started_at=started.isoformat(),
completed_at=completed.isoformat(),
finished_at=completed.isoformat(),
duration=duration,
status="success",
type="files",
message="Backup completed successfully"
)
except Exception as e:
completed = datetime.now()
duration = int((completed - started).total_seconds())

result = BackupResult(
file_name="",
file_path="",
file_size=0,
checksum="",
started_at=started.isoformat(),
completed_at=completed.isoformat(),
finished_at=completed.isoformat(),
duration=duration,
status="failed",
type="files",
message=f"Backup failed: {str(e)}"
)

response = store_job_report(job_id, result)

print(response)
return response

def run_backup(source_path: str | dict):
if isinstance(source_path, dict):
return run_backup_job(source_path)

started = datetime.now()

try:
filename = f"backup-{started.strftime('%Y%m%d-%H%M%S')}"

destination = str(Path(BACKUP_OUTPUT) / filename)
zip_file = create_zip(source_path, destination)
completed = datetime.now()
duration = int((completed - started).total_seconds())
zip_file = create_zip(source_path, destination)
completed = datetime.now()
duration = int((completed - started).total_seconds())

result = BackupResult(
file_name=Path(zip_file).name,
Expand All @@ -25,14 +83,15 @@ def run_backup(source_path: str):
checksum=sha256(zip_file),
started_at=started.isoformat(),
completed_at=completed.isoformat(),
finished_at=completed.isoformat(),
duration=duration,
status="success",
type="files",
message="Backup completed successfully"
)
except Exception as e:
completed = datetime.now()
duration = int((completed - started).total_seconds())
duration = int((completed - started).total_seconds())

result = BackupResult(
file_name="",
Expand All @@ -41,6 +100,7 @@ def run_backup(source_path: str):
checksum="",
started_at=started.isoformat(),
completed_at=completed.isoformat(),
finished_at=completed.isoformat(),
duration=duration,
status="failed",
type="files",
Expand Down
3 changes: 2 additions & 1 deletion models/backup_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ class BackupResult:
duration: int
status: str
type: str
message: str | None
message: str | None = None
finished_at: str | None = None