mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
Backups: export and download archives in one step
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
629e34f93c
commit
ff673b0d73
@ -132,6 +132,9 @@ def subcommand_extract(arguments):
|
||||
|
||||
def subcommand_export(arguments):
|
||||
"""Export archive contents as tarball."""
|
||||
# TODO: if this is only used for files in /tmp, add checks to verify that
|
||||
# arguments.filename is within /tmp
|
||||
# TODO: arguments.filename is not a filename but a path
|
||||
path = os.path.dirname(arguments.filename)
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
@ -47,6 +47,8 @@ service = None
|
||||
MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/'
|
||||
|
||||
BACKUP_FOLDER_NAME = 'FreedomBox-backups'
|
||||
# default backup path for temporary actions like imports or download
|
||||
TMP_BACKUP_PATH = '/tmp/freedombox-backup.tar.gz'
|
||||
|
||||
|
||||
def init():
|
||||
@ -108,10 +110,14 @@ def delete_archive(name):
|
||||
actions.superuser_run('backups', ['delete', '--name', name])
|
||||
|
||||
|
||||
def export_archive(name, location):
|
||||
location_path = get_location_path(location)
|
||||
filepath = get_archive_path(location_path,
|
||||
get_valid_filename(name) + '.tar.gz')
|
||||
def export_archive(name, location, tmp_dir=False):
|
||||
# TODO: find a better solution for distinguishing exports to /tmp
|
||||
if tmp_dir:
|
||||
filepath = TMP_BACKUP_PATH
|
||||
else:
|
||||
location_path = get_location_path(location)
|
||||
filepath = get_archive_path(location_path,
|
||||
get_valid_filename(name) + '.tar.gz')
|
||||
# TODO: that's a full path, not a filename; rename argument
|
||||
actions.superuser_run('backups',
|
||||
['export', '--name', name, '--filename', filepath])
|
||||
|
||||
@ -86,6 +86,10 @@
|
||||
href="{% url 'backups:export' archive.name %}">
|
||||
{% trans "Export" %}
|
||||
</a>
|
||||
<a class="archive-export btn btn-sm btn-default" target="_blank"
|
||||
href="{% url 'backups:export-and-download' archive.name %}">
|
||||
{% trans "Download" %}
|
||||
</a>
|
||||
<a class="archive-delete btn btn-sm btn-default"
|
||||
href="{% url 'backups:delete' archive.name %}">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true">
|
||||
|
||||
@ -21,7 +21,8 @@ URLs for the backups module.
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import IndexView, CreateArchiveView, DownloadArchiveView, \
|
||||
DeleteArchiveView, ExportArchiveView, RestoreView, UploadArchiveView
|
||||
DeleteArchiveView, ExportArchiveView, RestoreView, UploadArchiveView, \
|
||||
ExportAndDownloadView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^sys/backups/$', IndexView.as_view(), name='index'),
|
||||
@ -30,6 +31,8 @@ urlpatterns = [
|
||||
ExportArchiveView.as_view(), name='export'),
|
||||
url(r'^sys/backups/download/(?P<device>[^/]+)/(?P<name>[^/]+)/$',
|
||||
DownloadArchiveView.as_view(), name='download'),
|
||||
url(r'^sys/backups/export-and-download/(?P<name>[^/]+)/$',
|
||||
ExportAndDownloadView.as_view(), name='export-and-download'),
|
||||
url(r'^sys/backups/delete/(?P<name>[^/]+)/$',
|
||||
DeleteArchiveView.as_view(), name='delete'),
|
||||
url(r'^sys/backups/upload/$', UploadArchiveView.as_view(), name='upload'),
|
||||
|
||||
@ -19,6 +19,7 @@ Views for the backups app.
|
||||
"""
|
||||
|
||||
import mimetypes
|
||||
import os
|
||||
from datetime import datetime
|
||||
from urllib.parse import unquote
|
||||
|
||||
@ -33,7 +34,7 @@ from django.views.generic import View, FormView, TemplateView
|
||||
|
||||
from plinth.modules import backups
|
||||
|
||||
from . import api, find_exported_archive, forms
|
||||
from . import api, find_exported_archive, TMP_BACKUP_PATH, forms
|
||||
|
||||
|
||||
subsubmenu = [{
|
||||
@ -131,6 +132,39 @@ def _get_file_response(path, filename):
|
||||
return response
|
||||
|
||||
|
||||
class ExportAndDownloadView(View):
|
||||
"""View to export and download an archive."""
|
||||
def get(self, request, name):
|
||||
name = unquote(name)
|
||||
with create_temporary_backup_file(name) as filename, \
|
||||
open(filename, 'rb') as file_handle:
|
||||
(content_type, encoding) = mimetypes.guess_type(filename)
|
||||
response = HttpResponse(File(file_handle),
|
||||
content_type=content_type)
|
||||
content_disposition = 'attachment; filename="%s"' % filename
|
||||
response['Content-Disposition'] = content_disposition
|
||||
if encoding:
|
||||
response['Content-Encoding'] = encoding
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class create_temporary_backup_file:
|
||||
"""Create a temporary backup file that gets deleted after using it"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.path = TMP_BACKUP_PATH
|
||||
|
||||
def __enter__(self):
|
||||
backups.export_archive(self.name, self.path, tmp_dir=True)
|
||||
return self.path
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
if os.path.isfile(self.path):
|
||||
os.remove(self.path)
|
||||
|
||||
|
||||
class UploadArchiveView(SuccessMessageMixin, FormView):
|
||||
form_class = forms.UploadForm
|
||||
prefix = 'backups'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user