Add admin database backup export and import
This commit is contained in:
+54
-3
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user