1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-12-22 17:10:10 +00:00

backups: upload and restore backup in-app

This commit is contained in:
Harvey Tindall 2023-12-21 18:12:58 +00:00
parent eff313be41
commit ade032241a
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
5 changed files with 51 additions and 3 deletions

26
api.go
View File

@ -611,8 +611,7 @@ func (app *appContext) GetBackups(gc *gin.Context) {
// @Summary Restore a backup file stored locally to the server. // @Summary Restore a backup file stored locally to the server.
// @Param fname path string true "backup filename" // @Param fname path string true "backup filename"
// @Router /backups/restore/{fname} [get] // @Router /backups/restore/{fname} [post]
// @Produce octet-stream
// @Produce json // @Produce json
// @Failure 400 {object} boolResponse // @Failure 400 {object} boolResponse
// @Security Bearer // @Security Bearer
@ -632,3 +631,26 @@ func (app *appContext) RestoreLocalBackup(gc *gin.Context) {
LOADBAK = fullpath LOADBAK = fullpath
app.restart(gc) app.restart(gc)
} }
// @Summary Restore a backup file uploaded by the user.
// @Param file formData file true ".bak file"
// @Router /backups/restore [post]
// @Produce json
// @Failure 400 {object} boolResponse
// @Security Bearer
// @tags Other
func (app *appContext) RestoreBackup(gc *gin.Context) {
file, err := gc.FormFile("backups-file")
if err != nil {
app.err.Printf("Failed to get file from form data: %v\n", err)
respondBool(400, false, gc)
return
}
app.debug.Printf("Got uploaded file \"%s\"\n", file.Filename)
path := app.config.Section("backups").Key("path").String()
fullpath := filepath.Join(path, "jfa-go-upload-bak-"+time.Now().Local().Format(BACKUP_DATEFMT)+BACKUP_SUFFIX)
gc.SaveUploadedFile(file, fullpath)
app.debug.Printf("Saved to \"%s\"\n", fullpath)
LOADBAK = fullpath
app.restart(gc)
}

View File

@ -336,6 +336,7 @@
<div class="row col flex"> <div class="row col flex">
<button class="button ~info @low mr-2 mt-4 mb-4" id="settings-backups-backup">{{ .strings.backupNow }}</button> <button class="button ~info @low mr-2 mt-4 mb-4" id="settings-backups-backup">{{ .strings.backupNow }}</button>
<button class="button ~neutral @low mr-2 mt-4 mb-4" id="settings-backups-upload">{{ .strings.backupUpload }}</button> <button class="button ~neutral @low mr-2 mt-4 mb-4" id="settings-backups-upload">{{ .strings.backupUpload }}</button>
<input id="backups-file" name="backups-file" type="file" hidden>
<button class="button ~neutral @low mr-2 mt-4 mb-4" id="settings-backups-sort-direction">{{ .strings.sortDirection }}</button> <button class="button ~neutral @low mr-2 mt-4 mb-4" id="settings-backups-sort-direction">{{ .strings.sortDirection }}</button>
</div> </div>
<div class="overflow-x-auto"> <div class="overflow-x-auto">

View File

@ -212,6 +212,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
api.GET(p+"/backups/:fname", app.GetBackup) api.GET(p+"/backups/:fname", app.GetBackup)
api.GET(p+"/backups", app.GetBackups) api.GET(p+"/backups", app.GetBackups)
api.POST(p+"/backups/restore/:fname", app.RestoreLocalBackup) api.POST(p+"/backups/restore/:fname", app.RestoreLocalBackup)
api.POST(p+"/backups/restore", app.RestoreBackup)
if telegramEnabled || discordEnabled || matrixEnabled { if telegramEnabled || discordEnabled || matrixEnabled {
api.GET(p+"/telegram/pin", app.TelegramGetPin) api.GET(p+"/telegram/pin", app.TelegramGetPin)
api.GET(p+"/telegram/verified/:pin", app.TelegramVerified) api.GET(p+"/telegram/verified/:pin", app.TelegramVerified)

View File

@ -56,6 +56,15 @@ export const _download = (url: string, fname: string): void => {
req.send(); req.send();
}; };
export const _upload = (url: string, formData: FormData): void => {
let req = new XMLHttpRequest();
if (window.URLBase) { url = window.URLBase + url; }
req.open("POST", url, true);
req.setRequestHeader("Authorization", "Bearer " + window.token);
// req.setRequestHeader('Content-Type', 'multipart/form-data');
req.send(formData);
};
export const _post = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, response?: boolean, statusHandler?: (req: XMLHttpRequest) => void): void => { export const _post = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, response?: boolean, statusHandler?: (req: XMLHttpRequest) => void): void => {
let req = new XMLHttpRequest(); let req = new XMLHttpRequest();
req.open("POST", window.URLBase + url, true); req.open("POST", window.URLBase + url, true);

View File

@ -1,4 +1,4 @@
import { _get, _post, _delete, _download, toggleLoader, addLoader, removeLoader, insertText, toClipboard, toDateString } from "../modules/common.js"; import { _get, _post, _delete, _download, _upload, toggleLoader, addLoader, removeLoader, insertText, toClipboard, toDateString } from "../modules/common.js";
import { Marked } from "@ts-stack/markdown"; import { Marked } from "@ts-stack/markdown";
import { stripMarkdown } from "../modules/stripmd.js"; import { stripMarkdown } from "../modules/stripmd.js";
@ -830,6 +830,21 @@ export class settingsList {
}; };
this._backupSortDirection.onclick = () => this.setBackupSort(!(this._backupSortAscending)); this._backupSortDirection.onclick = () => this.setBackupSort(!(this._backupSortAscending));
const advancedEnableToggle = document.getElementById("settings-advanced-enabled") as HTMLInputElement; const advancedEnableToggle = document.getElementById("settings-advanced-enabled") as HTMLInputElement;
const filedlg = document.getElementById("backups-file") as HTMLInputElement;
document.getElementById("settings-backups-upload").onclick = () => {
filedlg.click();
};
filedlg.addEventListener("change", () => {
if (filedlg.files.length == 0) return;
const form = new FormData();
form.append("backups-file", filedlg.files[0], filedlg.files[0].name);
_upload("/backups/restore", form);
window.modals.backups.close();
window.modals.settingsRefresh.modal.querySelector("span.heading").textContent = window.lang.strings("settingsRestarting");
window.modals.settingsRefresh.show();
});
advancedEnableToggle.onchange = () => { advancedEnableToggle.onchange = () => {
document.dispatchEvent(new CustomEvent("settings-advancedState", { detail: advancedEnableToggle.checked })); document.dispatchEvent(new CustomEvent("settings-advancedState", { detail: advancedEnableToggle.checked }));
const parent = advancedEnableToggle.parentElement; const parent = advancedEnableToggle.parentElement;