Backups: export and download archives in one step

Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Michael Pimmer 2018-09-17 05:53:57 +00:00 committed by James Valleroy
parent 629e34f93c
commit ff673b0d73
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
5 changed files with 56 additions and 6 deletions

View File

@ -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)

View File

@ -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])

View File

@ -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">

View File

@ -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'),

View File

@ -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'