Add admin database backup export and import

This commit is contained in:
orohi
2026-07-25 01:22:30 +03:00
parent 7842ddf1b8
commit 06599e58f1
4 changed files with 269 additions and 3 deletions
+54 -3
View File
@@ -2,12 +2,14 @@ from __future__ import annotations
import secrets
from datetime import datetime, timezone
from urllib.parse import quote, unquote
from fastapi import APIRouter, Depends, Form, HTTPException, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi import APIRouter, Depends, File, Form, HTTPException, Request, UploadFile
from fastapi.responses import HTMLResponse, RedirectResponse, Response
from fastapi.templating import Jinja2Templates
from auth import check_admin_credentials, get_current_admin, require_admin_page
from backup import dumps_backup, export_backup, import_backup, loads_backup
from config import get_settings
from database import get_db
from urlutil import public_base_url
@@ -121,6 +123,10 @@ async def admin_dashboard(request: Request):
page = total_pages
site_base = public_base_url(request)
flash = request.query_params.get("flash")
flash_error = request.query_params.get("flash_error")
if flash_error:
flash_error = unquote(flash_error)
return templates.TemplateResponse(
"admin/dashboard.html",
{
@@ -128,7 +134,8 @@ async def admin_dashboard(request: Request):
"brand": settings.brand_name,
"links": links,
"site_base": site_base,
"flash": request.query_params.get("flash"),
"flash": flash,
"flash_error": flash_error,
"tab": tab,
"page": page,
"per_page": per_page,
@@ -293,3 +300,47 @@ async def link_detail(request: Request, link_id: int):
"site_base": public_base_url(request),
},
)
@router.get("/backup/export")
async def backup_export(_admin: str = Depends(get_current_admin)):
payload = await export_backup()
stamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
filename = f"remna-backup-{stamp}.json"
return Response(
content=dumps_backup(payload),
media_type="application/json; charset=utf-8",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
@router.post("/backup/import")
async def backup_import(
file: UploadFile = File(...),
_admin: str = Depends(get_current_admin),
):
raw = await file.read()
if not raw:
return RedirectResponse(
"/admin?type=all&flash_error=" + quote("пустой файл"),
status_code=303,
)
if len(raw) > 20 * 1024 * 1024:
return RedirectResponse(
"/admin?type=all&flash_error=" + quote("файл слишком большой (макс 20 МБ)"),
status_code=303,
)
try:
data = loads_backup(raw)
await import_backup(data)
except ValueError as exc:
return RedirectResponse(
"/admin?type=all&flash_error=" + quote(str(exc)[:200]),
status_code=303,
)
except Exception as exc:
return RedirectResponse(
"/admin?type=all&flash_error=" + quote(str(exc)[:200]),
status_code=303,
)
return RedirectResponse("/admin?type=all&flash=imported", status_code=303)