diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..dbb447a --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +APP_NAME="Nova System Admin" + +API_URL= +API_KEY= + +TEST_BACKUP_PATH= diff --git a/api/report.py b/api/report.py index b8521f3..befe317 100644 --- a/api/report.py +++ b/api/report.py @@ -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( diff --git a/app.py b/app.py index f4dc551..95b54e2 100644 --- a/app.py +++ b/app.py @@ -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() diff --git a/backup/backup.py b/backup/backup.py index 8651f7a..0caff51 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -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, @@ -25,6 +83,7 @@ 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", @@ -32,7 +91,7 @@ def run_backup(source_path: str): ) except Exception as e: completed = datetime.now() - duration = int((completed - started).total_seconds()) + duration = int((completed - started).total_seconds()) result = BackupResult( file_name="", @@ -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", diff --git a/models/backup_result.py b/models/backup_result.py index 0a0a8a7..d4bdc43 100644 --- a/models/backup_result.py +++ b/models/backup_result.py @@ -11,5 +11,6 @@ class BackupResult: duration: int status: str type: str - message: str | None + message: str | None = None + finished_at: str | None = None \ No newline at end of file