diff --git a/.gitignore b/.gitignore
index d686ae120..e33b1579d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,6 +43,8 @@ docker
# fly.io
fly.toml
+lnbits-backup.zip
+
# Ignore extensions (post installable extension PR)
extensions/
upgrades/
diff --git a/lnbits/core/templates/admin/index.html b/lnbits/core/templates/admin/index.html
index 96e04fe61..4ddca6c0e 100644
--- a/lnbits/core/templates/admin/index.html
+++ b/lnbits/core/templates/admin/index.html
@@ -47,11 +47,11 @@
Add funds to a wallet.
-
+ >
{
- this.$q.notify({
- type: 'positive',
- message:
- 'Success! Database backup request, download starts soon!',
- icon: null
- })
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
+ window.open('/admin/api/v1/backup/?usr=' + this.g.user.id, '_blank')
}
}
})
diff --git a/lnbits/core/views/admin_api.py b/lnbits/core/views/admin_api.py
index 4a2c5a2bc..fbf848a85 100644
--- a/lnbits/core/views/admin_api.py
+++ b/lnbits/core/views/admin_api.py
@@ -1,7 +1,13 @@
+import os
+import time
from http import HTTPStatus
+from shutil import make_archive
+from subprocess import Popen
from typing import Optional
+from urllib.parse import urlparse
from fastapi import Body, Depends
+from fastapi.responses import FileResponse
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_wallet
@@ -9,7 +15,7 @@ from lnbits.core.models import User
from lnbits.core.services import update_cached_settings, update_wallet_balance
from lnbits.decorators import check_admin, check_super_user
from lnbits.server import server_restart
-from lnbits.settings import AdminSettings, EditableSettings
+from lnbits.settings import AdminSettings, EditableSettings, settings
from .. import core_app
from ..crud import delete_admin_settings, get_admin_settings, update_admin_settings
@@ -72,3 +78,43 @@ async def api_topup_balance(
await update_wallet_balance(wallet_id=id, amount=int(amount))
return {"status": "Success"}
+
+
+@core_app.get(
+ "/admin/api/v1/backup/",
+ status_code=HTTPStatus.OK,
+ dependencies=[Depends(check_super_user)],
+ response_class=FileResponse,
+)
+async def api_download_backup() -> FileResponse:
+ last_filename = "lnbits-backup"
+ filename = f"lnbits-backup-{int(time.time())}.zip"
+ db_url = settings.lnbits_database_url
+ pg_backup_filename = f"{settings.lnbits_data_folder}/lnbits-database.dmp"
+ is_pg = db_url and db_url.startswith("postgres://")
+
+ if is_pg:
+ p = urlparse(db_url)
+ command = (
+ f"pg_dump --host={p.hostname} "
+ f'--dbname={p.path.replace("/", "")} '
+ f"--username={p.username} "
+ f"--no-password "
+ f"--format=c "
+ f"--file={pg_backup_filename}"
+ )
+ proc = Popen(
+ command, shell=True, env={**os.environ, "PGPASSWORD": p.password or ""}
+ )
+ proc.wait()
+
+ make_archive(last_filename, "zip", settings.lnbits_data_folder)
+
+ # cleanup pg_dump file
+ if is_pg:
+ proc = Popen(f"rm {pg_backup_filename}", shell=True)
+ proc.wait()
+
+ return FileResponse(
+ path=f"{last_filename}.zip", filename=filename, media_type="application/zip"
+ )