diff --git a/api.go b/api.go
index 97d54a9..9ff95c8 100644
--- a/api.go
+++ b/api.go
@@ -611,8 +611,7 @@ func (app *appContext) GetBackups(gc *gin.Context) {
// @Summary Restore a backup file stored locally to the server.
// @Param fname path string true "backup filename"
-// @Router /backups/restore/{fname} [get]
-// @Produce octet-stream
+// @Router /backups/restore/{fname} [post]
// @Produce json
// @Failure 400 {object} boolResponse
// @Security Bearer
@@ -632,3 +631,26 @@ func (app *appContext) RestoreLocalBackup(gc *gin.Context) {
LOADBAK = fullpath
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)
+}
diff --git a/html/admin.html b/html/admin.html
index cad852b..1a9486a 100644
--- a/html/admin.html
+++ b/html/admin.html
@@ -336,6 +336,7 @@
+
diff --git a/router.go b/router.go
index fa49348..0a0cc9c 100644
--- a/router.go
+++ b/router.go
@@ -212,6 +212,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
api.GET(p+"/backups/:fname", app.GetBackup)
api.GET(p+"/backups", app.GetBackups)
api.POST(p+"/backups/restore/:fname", app.RestoreLocalBackup)
+ api.POST(p+"/backups/restore", app.RestoreBackup)
if telegramEnabled || discordEnabled || matrixEnabled {
api.GET(p+"/telegram/pin", app.TelegramGetPin)
api.GET(p+"/telegram/verified/:pin", app.TelegramVerified)
diff --git a/ts/modules/common.ts b/ts/modules/common.ts
index 9de70fd..7ce95e8 100644
--- a/ts/modules/common.ts
+++ b/ts/modules/common.ts
@@ -56,6 +56,15 @@ export const _download = (url: string, fname: string): void => {
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 => {
let req = new XMLHttpRequest();
req.open("POST", window.URLBase + url, true);
diff --git a/ts/modules/settings.ts b/ts/modules/settings.ts
index 4244abf..2dc1e10 100644
--- a/ts/modules/settings.ts
+++ b/ts/modules/settings.ts
@@ -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 { stripMarkdown } from "../modules/stripmd.js";
@@ -830,6 +830,21 @@ export class settingsList {
};
this._backupSortDirection.onclick = () => this.setBackupSort(!(this._backupSortAscending));
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 = () => {
document.dispatchEvent(new CustomEvent("settings-advancedState", { detail: advancedEnableToggle.checked }));
const parent = advancedEnableToggle.parentElement;