From 21c9e321b368471b5fcbee21ca2a2eec5b3f4ee7 Mon Sep 17 00:00:00 2001 From: Graham Steffaniak <42989099+gtsteffaniak@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:58:28 -0500 Subject: [PATCH] Hotfix: user creation and other api url issues (#240) --- backend/http/users.go | 13 +++++++++++-- frontend/src/api/files.js | 23 ++++++++--------------- frontend/src/api/share.js | 18 ++++++++++-------- frontend/src/api/users.js | 3 ++- frontend/src/api/utils.js | 10 ++++++---- frontend/src/notify/message.js | 2 +- frontend/src/views/Settings.vue | 9 +++++++-- 7 files changed, 45 insertions(+), 33 deletions(-) diff --git a/backend/http/users.go b/backend/http/users.go index 60b94262..bcae5319 100644 --- a/backend/http/users.go +++ b/backend/http/users.go @@ -2,6 +2,7 @@ package http import ( "encoding/json" + "fmt" "io" "net/http" "reflect" @@ -118,8 +119,16 @@ func userDeleteHandler(w http.ResponseWriter, r *http.Request, d *requestContext num, _ := strconv.ParseUint(givenUserIdString, 10, 32) givenUserId := uint(num) - if givenUserId == d.user.ID || !d.user.Perm.Admin { - return http.StatusForbidden, nil + if givenUserId == d.user.ID { + return http.StatusForbidden, fmt.Errorf("cannot delete your own user") + } + + if !d.user.Perm.Admin { + return http.StatusForbidden, fmt.Errorf("cannot delete users without admin permissions") + } + + if givenUserId == 1 { + return http.StatusForbidden, fmt.Errorf("cannot delete the default admin user") } // Delete the user diff --git a/frontend/src/api/files.js b/frontend/src/api/files.js index 87de7ed4..ea447204 100644 --- a/frontend/src/api/files.js +++ b/frontend/src/api/files.js @@ -1,6 +1,6 @@ import { createURL, fetchURL, adjustedData} from "./utils"; import { baseURL } from "@/utils/constants"; -import { removePrefix,getApiPath } from "@/utils/url.js"; +import { removePrefix, getApiPath } from "@/utils/url.js"; import { state } from "@/store"; import { notify } from "@/notify"; @@ -53,25 +53,18 @@ export async function put(url, content = "") { export function download(format, ...files) { try { - let url = `${baseURL}/api/raw`; + let path = ""; + let fileargs = ""; if (files.length === 1) { - url += "?path="+removePrefix(files[0], "files"); + path = removePrefix(files[0], "files") } else { - let arg = ""; - for (let file of files) { - arg += removePrefix(file,"files") + ","; + fileargs += removePrefix(file,"files") + ","; } - - arg = arg.substring(0, arg.length - 1); - arg = encodeURIComponent(arg); - url += `?files=${arg}`; + fileargs = fileargs.substring(0, fileargs.length - 1); } - - if (format) { - url += `&algo=${format}`; - } - + const apiPath = getApiPath("api/raw",{path: path, files: fileargs, algo: format}); + let url = `${baseURL}${apiPath}`; window.open(url); } catch (err) { notify.showError(err.message || "Error downloading files"); diff --git a/frontend/src/api/share.js b/frontend/src/api/share.js index 44080495..147c4d0f 100644 --- a/frontend/src/api/share.js +++ b/frontend/src/api/share.js @@ -1,15 +1,17 @@ import { fetchURL, fetchJSON, createURL, adjustedData } from "./utils"; import { notify } from "@/notify"; +import { getApiPath } from "@/utils/url.js"; export async function list() { - return fetchJSON("api/shares"); + const apiPath = getApiPath("api/shares"); + return fetchJSON(apiPath); } export async function get(path, hash) { try { const params = { path, hash }; - const url = createURL(`api/share`, params, false); - let data = fetchJSON(url); + const apiPath = getApiPath("api/share",params); + let data = fetchJSON(apiPath); return adjustedData(data, `api/share${path}`); } catch (err) { notify.showError(err.message || "Error fetching data"); @@ -19,25 +21,25 @@ export async function get(path, hash) { export async function remove(hash) { const params = { hash }; - const url = createURL(`api/share`, params, false); - await fetchURL(url, { + const apiPath = getApiPath("api/share",params); + await fetchURL(apiPath, { method: "DELETE", }); } export async function create(path, password = "", expires = "", unit = "hours") { const params = { path }; - const url = createURL(`api/share`, params, false); + const apiPath = getApiPath("api/share",params); let body = "{}"; if (password != "" || expires !== "" || unit !== "hours") { body = JSON.stringify({ password: password, expires: expires, unit: unit }); } - return fetchJSON(url, { + return fetchJSON(apiPath, { method: "POST", body: body, }); } export function getShareURL(share) { - return createURL("share/" + share.hash, {}, false); + return createURL("share/"+share.hash, {}, false); } diff --git a/frontend/src/api/users.js b/frontend/src/api/users.js index 110d4c38..b21ecf3d 100644 --- a/frontend/src/api/users.js +++ b/frontend/src/api/users.js @@ -60,7 +60,8 @@ export function deleteApiKey(params) { export async function create(user) { try { - const res = await fetchURL(`api/users`, { + const apiPath = getApiPath("api/users"); + const res = await fetchURL(apiPath, { method: "POST", body: JSON.stringify({ what: "user", diff --git a/frontend/src/api/utils.js b/frontend/src/api/utils.js index 67e474c2..a824eec7 100644 --- a/frontend/src/api/utils.js +++ b/frontend/src/api/utils.js @@ -24,9 +24,11 @@ export async function fetchURL(url, opts, auth = true) { ...rest, }); } catch (e) { - console.error(e) - const error = new Error("000 No connection"); - error.status = res.status; + let message = e; + if (e == "TypeError: Failed to fetch") { + message = "Failed to connect to the server, is it still running?"; + } + const error = new Error(message); throw error; } @@ -35,7 +37,7 @@ export async function fetchURL(url, opts, auth = true) { } if (res.status < 200 || res.status > 299) { - const error = new Error(await res.text()); + let error = new Error(await res.text()); error.status = res.status; if (auth && res.status == 401) { diff --git a/frontend/src/notify/message.js b/frontend/src/notify/message.js index 188bb6a1..5282d4bd 100644 --- a/frontend/src/notify/message.js +++ b/frontend/src/notify/message.js @@ -16,7 +16,7 @@ export function showPopup(type, message) { if (apiMessage && Object.prototype.hasOwnProperty.call(apiMessage, "status") && Object.prototype.hasOwnProperty.call(apiMessage, "message")) { - popupContent.textContent = "Errors " + apiMessage.status + ": " + apiMessage.message; + popupContent.textContent = "Error " + apiMessage.status + ": " + apiMessage.message; } } catch (error) { popupContent.textContent = message; diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue index 6ab49d59..a4168a74 100644 --- a/frontend/src/views/Settings.vue +++ b/frontend/src/views/Settings.vue @@ -5,7 +5,7 @@ v-for="setting in settings" :key="setting.id + '-main'" :id="setting.id + '-main'" - @click="setView(setting.id + '-main')" + @click="handleClick($event, setting.id + '-main')" > @@ -77,13 +77,18 @@ export default { methods: { shouldShow(setting) { const perm = setting?.perm || {}; - // Check if all keys in setting.perm exist in state.user.perm and have truthy values return Object.keys(perm).every((key) => state.user.perm[key]); }, setView(view) { if (state.activeSettingsView === view) return; mutations.setActiveSettingsView(view); }, + handleClick(event, view) { + // Allow propagation if the click is on a link or a child element with default behavior + const target = event.target.closest("a, router-link"); + if (target) return; // Let the browser/router handle the navigation + this.setView(view); // Call the setView method for other clicks + }, }, };