Files
mostovik-backend/src/apps/backups/admin.py
Aleksandr Meshchriakov 92d5ff4252
Some checks failed
CI/CD Pipeline / Code Quality Checks (push) Failing after 2m8s
CI/CD Pipeline / Run Tests (push) Successful in 2m52s
CI/CD Pipeline / Run API Inventory E2E Tests (push) Successful in 44s
CI/CD Pipeline / Telegram Notify Success (push) Has been skipped
Move export and upload actions to admin dashboard
2026-04-22 10:12:08 +02:00

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)