Files
ihrm/horilla_backup/views.py

369 lines
12 KiB
Python

import json
import os
from django.conf import settings
from django.contrib import messages
from django.db import connection
from django.shortcuts import redirect, render
from django.utils.translation import gettext_lazy as _
from horilla.decorators import (
hx_request_required,
login_required,
manager_can_enter,
owner_can_enter,
permission_required,
)
from .forms import *
from .gdrive import *
from .pgdump import *
from .scheduler import *
from .zip import *
# @login_required
# @permission_required("backup.add_localbackup")
# def local_setup(request):
# """
# function used to setup local backup.
# Parameters:
# request (HttpRequest): The HTTP request object.
# Returns:
# GET : return local backup setup template
# POST : return settings
# """
# form = LocalBackupSetupForm()
# show = False
# active = False
# if LocalBackup.objects.exists():
# form = LocalBackupSetupForm(instance=LocalBackup.objects.first())
# show = True
# active = LocalBackup.objects.first().active
# if request.method == "POST":
# form = LocalBackupSetupForm(request.POST, request.FILES)
# if form.is_valid():
# form.save()
# stop_backup_job()
# messages.success(request, _("Local backup automation setup updated."))
# return redirect("local")
# return render(request, "backup/local_setup_form.html", {"form": form, "show":show, "active":active})
# @login_required
# @permission_required("backup.change_localbackup")
# def local_Backup_stop_or_start(request):
# """
# function used to stop or start local backup.
# Parameters:
# request (HttpRequest): The HTTP request object.
# Returns:
# GET : return local backup setup template
# POST : return settings
# """
# if LocalBackup.objects.exists():
# local_backup = LocalBackup.objects.first()
# if local_backup.active == True:
# local_backup.active = False
# stop_backup_job()
# message = "Local Backup Automation Stopped Successfully."
# else:
# local_backup.active = True
# start_backup_job()
# message = "Local Backup Automation Started Successfully."
# local_backup.save()
# messages.success(request, _(message))
# return redirect("local")
# @login_required
# @permission_required("backup.delete_localbackup")
# def local_Backup_delete(request):
# """
# function used to delete local backup.
# Parameters:
# request (HttpRequest): The HTTP request object.
# Returns:
# GET : return local backup setup template
# POST : return settings
# """
# if LocalBackup.objects.exists():
# local_backup = LocalBackup.objects.first()
# local_backup.delete()
# stop_backup_job()
# messages.success(request, _("Local Backup Automation Removed Successfully."))
# return redirect("local")
@login_required
@permission_required("backup.add_localbackup")
def gdrive_setup(request):
"""
function used to setup gdrive backup.
Parameters:
request (HttpRequest): The HTTP request object.
Returns:
GET : return gdrive backup setup template
POST : return gdrive backup update template
"""
form = GdriveBackupSetupForm()
show = False
active = False
needs_auth = False
if connection.vendor != "postgresql":
return render(request, "backup/404.html")
if GoogleDriveBackup.objects.exists():
instance = GoogleDriveBackup.objects.first()
form = GdriveBackupSetupForm(instance=instance)
show = True
active = GoogleDriveBackup.objects.first().active
# Check if OAuth tokens exist - need both credentials file and access token
if instance.oauth_credentials_file:
if not instance.access_token:
needs_auth = True
else:
# No credentials file uploaded yet
needs_auth = False # Don't show auth button if no credentials file
if request.method == "POST":
form = GdriveBackupSetupForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
google_drive = form.save()
google_drive.active = False
google_drive.save()
stop_gdrive_backup_job()
# If credentials file was updated, reset tokens
if "oauth_credentials_file" in request.FILES:
google_drive.access_token = None
google_drive.refresh_token = None
google_drive.token_expiry = None
google_drive.save()
messages.success(
request,
_(
"OAuth credentials file uploaded. Please authorize Google Drive access."
),
)
else:
messages.success(
request, _("gdrive backup automation setup updated.")
)
return redirect("gdrive")
# Re-check needs_auth after potential updates
if instance.oauth_credentials_file and not instance.access_token:
needs_auth = True
return render(
request,
"backup/gdrive_setup_form.html",
{"form": form, "show": show, "active": active, "needs_auth": needs_auth},
)
if request.method == "POST":
form = GdriveBackupSetupForm(request.POST, request.FILES)
if form.is_valid():
google_drive = form.save()
# After saving, check if tokens are needed
if not google_drive.access_token:
needs_auth = True
messages.success(request, _("gdrive backup automation setup Created."))
return redirect("gdrive")
return render(
request,
"backup/gdrive_setup_form.html",
{"form": form, "show": show, "active": active, "needs_auth": needs_auth},
)
@login_required
@permission_required("backup.change_localbackup")
def gdrive_Backup_stop_or_start(request):
"""
function used to stop or start gdrive backup.
Parameters:
request (HttpRequest): The HTTP request object.
Returns:
GET : return gdrive backup setup template
POST : return gdrive backup update template
"""
if GoogleDriveBackup.objects.exists():
gdive_backup = GoogleDriveBackup.objects.first()
if gdive_backup.active == True:
gdive_backup.active = False
stop_gdrive_backup_job()
message = "Gdrive Backup Automation Stopped Successfully."
else:
gdive_backup.active = True
start_gdrive_backup_job()
message = "Gdrive Backup Automation Started Successfully."
gdive_backup.save()
messages.success(request, _(message))
return redirect("gdrive")
@login_required
@permission_required("backup.delete_localbackup")
def gdrive_Backup_delete(request):
"""
function used to delete gdrive backup.
Parameters:
request (HttpRequest): The HTTP request object.
Returns:
GET : return gdrive backup setup template
"""
if GoogleDriveBackup.objects.exists():
gdrive_backup = GoogleDriveBackup.objects.first()
gdrive_backup.delete()
stop_gdrive_backup_job()
messages.success(request, _("Gdrive Backup Automation Removed Successfully."))
return redirect("gdrive")
@login_required
@permission_required("backup.add_localbackup")
def gdrive_authorize(request):
"""
Initiate OAuth authorization flow for Google Drive.
"""
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" # For local development with HTTP
if not GoogleDriveBackup.objects.exists():
messages.error(request, _("Please set up Google Drive backup first."))
return redirect("gdrive")
google_drive = GoogleDriveBackup.objects.first()
if not google_drive.oauth_credentials_file:
messages.error(request, _("Please upload OAuth credentials file first."))
return redirect("gdrive")
try:
# Read OAuth credentials file
oauth_file_path = google_drive.oauth_credentials_file.path
with open(oauth_file_path, "r") as f:
client_config = json.load(f)
# Use redirect URI from credentials file if available, otherwise construct it
if (
"web" in client_config
and "redirect_uris" in client_config["web"]
and len(client_config["web"]["redirect_uris"]) > 0
):
redirect_uri = client_config["web"]["redirect_uris"][0]
else:
scheme = "https" if not settings.DEBUG else "http"
host = request.get_host()
redirect_uri = f"{scheme}://{host}/google/callback/"
# Store client config and redirect URI in session for callback
request.session["gdrive_oauth_client_config"] = json.dumps(client_config)
request.session["gdrive_oauth_redirect_uri"] = redirect_uri
# Generate authorization URL
authorization_url, flow, state = get_authorization_url(
oauth_file_path, redirect_uri
)
# Store state in session for verification
request.session["gdrive_oauth_state"] = state
return redirect(authorization_url)
except Exception as e:
messages.error(request, _(f"Failed to initiate authorization: {str(e)}"))
return redirect("gdrive")
@login_required
@permission_required("backup.add_localbackup")
def gdrive_callback(request):
"""
Handle OAuth callback from Google.
"""
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" # For local development with HTTP
if not GoogleDriveBackup.objects.exists():
messages.error(request, _("Google Drive backup not configured."))
return redirect("gdrive")
google_drive = GoogleDriveBackup.objects.first()
# Check for error in callback
if "error" in request.GET:
error = request.GET.get("error")
messages.error(request, _(f"Authorization failed: {error}"))
return redirect("gdrive")
# Check for authorization code
if "code" not in request.GET:
messages.error(request, _("Authorization code not received."))
return redirect("gdrive")
# Verify session data exists
if (
"gdrive_oauth_client_config" not in request.session
or "gdrive_oauth_redirect_uri" not in request.session
):
messages.error(request, _("Session expired. Please try authorizing again."))
return redirect("gdrive")
try:
# Recreate flow from stored client config
client_config = json.loads(request.session["gdrive_oauth_client_config"])
redirect_uri = request.session["gdrive_oauth_redirect_uri"]
flow = Flow.from_client_config(client_config, SCOPES, redirect_uri=redirect_uri)
# Exchange authorization code for tokens
full_url = request.build_absolute_uri()
flow.fetch_token(authorization_response=full_url)
creds = flow.credentials
# Store tokens in model
google_drive.access_token = creds.token
if creds.refresh_token:
google_drive.refresh_token = creds.refresh_token
if creds.expiry:
from django.utils import timezone
if timezone.is_naive(creds.expiry):
google_drive.token_expiry = timezone.make_aware(
creds.expiry, timezone.utc
)
else:
google_drive.token_expiry = creds.expiry
google_drive.save()
# Clean up session
if "gdrive_oauth_client_config" in request.session:
del request.session["gdrive_oauth_client_config"]
if "gdrive_oauth_redirect_uri" in request.session:
del request.session["gdrive_oauth_redirect_uri"]
if "gdrive_oauth_state" in request.session:
del request.session["gdrive_oauth_state"]
messages.success(request, _("Google Drive authorization successful!"))
return redirect("gdrive")
except Exception as e:
import traceback
print(f"OAuth callback error: {traceback.format_exc()}")
messages.error(request, _(f"Authorization failed: {str(e)}"))
return redirect("gdrive")