135 lines
4.8 KiB
Python
135 lines
4.8 KiB
Python
"""Admin для приложения backups."""
|
|
|
|
from apps.backups.models import BackupExportJob
|
|
from apps.backups.serializers import BackupExportRequestSerializer
|
|
from apps.backups.services import BackupExportError, BackupExportJobService
|
|
from django.contrib import admin
|
|
from django.contrib import messages
|
|
from django.http import HttpResponse
|
|
from django.shortcuts import redirect
|
|
from django.urls import path, reverse
|
|
from django.utils import timezone
|
|
|
|
|
|
@admin.register(BackupExportJob)
|
|
class BackupExportJobAdmin(admin.ModelAdmin):
|
|
"""Admin для задач backup-экспорта."""
|
|
|
|
list_display = [
|
|
"id",
|
|
"actual_date",
|
|
"status",
|
|
"task_id",
|
|
"requested_by",
|
|
"organizations_count",
|
|
"archive_size",
|
|
"started_at",
|
|
"completed_at",
|
|
]
|
|
list_filter = ["status", "actual_date", "created_at", "completed_at"]
|
|
search_fields = ["task_id", "checksum_sha256", "archive_filename"]
|
|
readonly_fields = [
|
|
"task_id",
|
|
"archive_path",
|
|
"archive_filename",
|
|
"checksum_filename",
|
|
"checksum_sha256",
|
|
"archive_size",
|
|
"organizations_count",
|
|
"error",
|
|
"started_at",
|
|
"completed_at",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
ordering = ["-actual_date", "-created_at"]
|
|
change_list_template = "admin/backups/backupexportjob/change_list.html"
|
|
|
|
def get_urls(self):
|
|
urls = super().get_urls()
|
|
custom_urls = [
|
|
path(
|
|
"export/",
|
|
self.admin_site.admin_view(self.export_view),
|
|
name="backups_backupexportjob_export",
|
|
),
|
|
]
|
|
return custom_urls + urls
|
|
|
|
def changelist_view(self, request, extra_context=None):
|
|
extra_context = extra_context or {}
|
|
extra_context["backup_export_url"] = reverse(
|
|
"admin:backups_backupexportjob_export"
|
|
)
|
|
extra_context["backup_export_default_date"] = timezone.localdate().isoformat()
|
|
return super().changelist_view(request, extra_context=extra_context)
|
|
|
|
def export_view(self, request):
|
|
changelist_url = reverse("admin:backups_backupexportjob_changelist")
|
|
|
|
if request.method != "POST":
|
|
self.message_user(
|
|
request,
|
|
"Выгрузка backup доступна только через POST.",
|
|
level=messages.WARNING,
|
|
)
|
|
return redirect(changelist_url)
|
|
|
|
serializer = BackupExportRequestSerializer(
|
|
data={"actual_date": request.POST.get("actual_date")}
|
|
)
|
|
if not serializer.is_valid():
|
|
self.message_user(
|
|
request,
|
|
f"Некорректная дата: {serializer.errors}",
|
|
level=messages.ERROR,
|
|
)
|
|
return redirect(changelist_url)
|
|
|
|
actual_date = serializer.validated_data.get("actual_date") or timezone.localdate()
|
|
try:
|
|
result = BackupExportJobService.check_or_start_job(
|
|
actual_date=actual_date,
|
|
requested_by_id=request.user.id if request.user.is_authenticated else None,
|
|
)
|
|
except BackupExportError as exc:
|
|
self.message_user(request, f"Ошибка запуска резервного экспорта: {exc}", level=messages.ERROR)
|
|
return redirect(changelist_url)
|
|
|
|
if result.action in {"started", "wait"}:
|
|
self.message_user(
|
|
request,
|
|
result.message,
|
|
level=messages.INFO
|
|
if result.action == "started"
|
|
else messages.WARNING,
|
|
)
|
|
return redirect(changelist_url)
|
|
|
|
if result.action == "download":
|
|
try:
|
|
artifact = BackupExportJobService.consume_ready_archive(
|
|
actual_date=result.actual_date
|
|
)
|
|
except BackupExportError as exc:
|
|
self.message_user(request, f"Ошибка загрузки backup: {exc}", level=messages.ERROR)
|
|
return redirect(changelist_url)
|
|
|
|
response = HttpResponse(artifact.archive_bytes, content_type="application/zip")
|
|
response.status_code = 200
|
|
response[
|
|
"Content-Disposition"
|
|
] = f'attachment; filename="{artifact.archive_filename}"'
|
|
response["X-Backup-SHA256"] = artifact.checksum_sha256
|
|
response["X-Backup-Checksum-File"] = artifact.checksum_filename
|
|
response["X-Backup-Organizations"] = str(artifact.organizations_count)
|
|
response["X-Backup-Actual-Date"] = artifact.actual_date.isoformat()
|
|
return response
|
|
|
|
self.message_user(
|
|
request,
|
|
f"Неожиданный статус задачи: {result.action}",
|
|
level=messages.ERROR,
|
|
)
|
|
return redirect(changelist_url)
|