unified upload button + download backups

This commit is contained in:
Hayden 2021-01-20 18:24:17 -09:00
commit eff9d6f559
8 changed files with 118 additions and 30 deletions

View file

@ -4,8 +4,7 @@ import mealplan from "./api/mealplan";
import settings from "./api/settings";
import themes from "./api/themes";
import migration from "./api/migration";
// import api from "../api";
import myUtils from "./api/upload";
export default {
recipes: recipe,
@ -14,4 +13,5 @@ export default {
settings: settings,
themes: themes,
migrations: migration,
utils: myUtils,
};

View file

@ -16,8 +16,8 @@ function processResponse(response) {
}
const apiReq = {
post: async function(url, data) {
let response = await axios.post(url, data).catch(function(error) {
post: async function (url, data) {
let response = await axios.post(url, data).catch(function (error) {
if (error.response) {
processResponse(error.response);
return error.response;
@ -27,8 +27,8 @@ const apiReq = {
return response;
},
get: async function(url, data) {
let response = await axios.get(url, data).catch(function(error) {
get: async function (url, data) {
let response = await axios.get(url, data).catch(function (error) {
if (error.response) {
processResponse(error.response);
return response;
@ -38,8 +38,8 @@ const apiReq = {
return response;
},
delete: async function(url, data) {
let response = await axios.delete(url, data).catch(function(error) {
delete: async function (url, data) {
let response = await axios.delete(url, data).catch(function (error) {
if (error.response) {
processResponse(error.response);
return response;

View file

@ -10,6 +10,7 @@ const backupURLs = {
createBackup: `${backupBase}export/database/`,
importBackup: (fileName) => `${backupBase}${fileName}/import/`,
deleteBackup: (fileName) => `${backupBase}${fileName}/delete/`,
downloadBackup: (fileName) => `${backupBase}${fileName}/download/`,
};
export default {
@ -32,4 +33,8 @@ export default {
let response = apiReq.post(backupURLs.createBackup, data);
return response;
},
async download(fileName) {
let response = await apiReq.get(backupURLs.downloadBackup(fileName));
return response.data;
},
};

View file

@ -0,0 +1,13 @@
import { apiReq } from "./api-utils";
export default {
// import api from "../api";
async uploadFile(url, fileObject) {
let response = await apiReq.post(url, fileObject, {
headers: {
"Content-Type": "multipart/form-data",
},
});
return response.data;
},
};

View file

@ -8,7 +8,7 @@
<v-card-text>
<v-row>
<v-col >
<v-col>
<v-checkbox
class="mb-n4 mt-1"
dense
@ -65,15 +65,15 @@
<v-divider></v-divider>
<v-card-actions>
<v-btn disabled color="success" text @click="raiseEvent('download')">
{{$t('general.download')}}
<v-btn color="success" text :href="`/api/backups/${name}/download/`">
{{ $t("general.download") }}
</v-btn>
<v-spacer></v-spacer>
<v-btn color="error" text @click="raiseEvent('delete')">
{{$t('general.delete')}}
{{ $t("general.delete") }}
</v-btn>
<v-btn color="success" text @click="raiseEvent('import')">
{{$t('general.import')}}
{{ $t("general.import") }}
</v-btn>
</v-card-actions>
</v-card>

View file

@ -21,10 +21,10 @@
Available Backups
<v-spacer></v-spacer>
<span>
<v-btn color="success" text class="ma-2 white--text">
Upload
<v-icon right dark> mdi-cloud-upload </v-icon>
</v-btn>
<UploadBtn
url="/api/backups/upload/"
@uploaded="getAvailableBackups"
/>
</span>
</v-card-title>
<AvailableBackupCard
@ -45,12 +45,14 @@
<script>
import api from "../../../api";
import SuccessFailureAlert from "../../UI/SuccessFailureAlert";
import UploadBtn from "../../UI/UploadBtn";
import AvailableBackupCard from "./AvailableBackupCard";
import NewBackupCard from "./NewBackupCard";
export default {
components: {
SuccessFailureAlert,
UploadBtn,
AvailableBackupCard,
NewBackupCard,
},
@ -70,6 +72,7 @@ export default {
let response = await api.backups.requestAvailable();
this.availableBackups = response.imports;
this.availableTemplates = response.templates;
console.log(this.availableBackups);
},
deleteBackup() {
if (this.$refs.form.validate()) {

View file

@ -1,24 +1,62 @@
<template>
<v-form ref="file">
<v-file-input
:loading="loading"
:label="$t('migration.upload-an-archive')"
v-model="file"
accept=".zip"
@change="upload"
:prepend-icon="icon"
class="file-icon"
<input ref="uploader" class="d-none" type="file" @change="onFileChanged" />
<v-btn
:loading="isSelecting"
@click="onButtonClick"
color="success"
text
class="ma-2 white--text"
>
</v-file-input>
<v-btn color="success" text class="ma-2 white--text">
<v-icon left dark> mdi-cloud-upload </v-icon>
Upload
<v-icon right dark> mdi-cloud-upload </v-icon>
</v-btn>
</v-form>
</template>
<script>
export default {};
import api from "../../api";
export default {
props: {
url: String,
},
data: () => ({
defaultButtonText: "Upload",
file: null,
isSelecting: false,
}),
methods: {
async upload() {
if (this.file != null) {
this.isSelecting = true;
let formData = new FormData();
formData.append("archive", this.file);
await api.utils.uploadFile(this.url, formData);
this.isSelecting = false;
this.$emit("uploaded");
}
},
onButtonClick() {
this.isSelecting = true;
window.addEventListener(
"focus",
() => {
this.isSelecting = false;
},
{ once: true }
);
this.$refs.uploader.click();
},
onFileChanged(e) {
this.file = e.target.files[0];
this.upload();
},
},
};
</script>
<style>

View file

@ -1,12 +1,14 @@
import operator
import shutil
from app_config import BACKUP_DIR, TEMPLATE_DIR
from db.db_setup import generate_session
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
from models.backup_models import BackupJob, ImportJob, Imports, LocalBackup
from services.backups.exports import backup_all
from services.backups.imports import ImportDatabase
from sqlalchemy.orm.session import Session
from starlette.responses import FileResponse
from utils.snackbar import SnackResponse
router = APIRouter(tags=["Import / Export"])
@ -49,6 +51,33 @@ def export_database(data: BackupJob, db: Session = Depends(generate_session)):
)
@router.post("/api/backups/upload/")
def upload_backup_zipfile(archive: UploadFile = File(...)):
""" Upload a .zip File to later be imported into Mealie """
dest = BACKUP_DIR.joinpath(archive.filename)
with dest.open("wb") as buffer:
shutil.copyfileobj(archive.file, buffer)
if dest.is_file:
return SnackResponse.success("Backup uploaded")
else:
return SnackResponse.error("Failure uploading file")
@router.get("/api/backups/{file_name}/download/")
def upload_nextcloud_zipfile(file_name: str):
""" Upload a .zip File to later be imported into Mealie """
file = BACKUP_DIR.joinpath(file_name)
if file.is_file:
return FileResponse(
file, media_type="application/octet-stream", filename=file_name
)
else:
return SnackResponse.error("No File Found")
@router.post("/api/backups/{file_name}/import/", status_code=200)
def import_database(
file_name: str, import_data: ImportJob, db: Session = Depends(generate_session)