From 38c11567ef56b95fd45dd2111390e0ef5f91168d Mon Sep 17 00:00:00 2001 From: DecDuck Date: Sun, 21 Jun 2026 20:07:59 +1000 Subject: [PATCH] Variety of bug fixes (#432) * Fix #414 * Implement #268 * Add #269 --- server/composables/user.ts | 7 ++- server/i18n/locales/en_us.json | 3 + server/layouts/admin.vue | 65 +++++++++++++++----- server/middleware/require-user.global.ts | 3 +- server/pages/admin/library/sources/index.vue | 57 ++++++++++------- 5 files changed, 97 insertions(+), 38 deletions(-) diff --git a/server/composables/user.ts b/server/composables/user.ts index c29b0694..463640dd 100644 --- a/server/composables/user.ts +++ b/server/composables/user.ts @@ -9,7 +9,12 @@ export const updateUser = async () => { const user = useUser(); if (user.value === null) return; - user.value = await $dropFetch("/api/v1/user"); + user.value = await $dropFetch("/api/v1/user", { + // Forward headers manually when called outside a component + headers: import.meta.server + ? useRequestHeaders(["cookie", "authorization"]) + : undefined, + }); }; export async function completeSignin() { diff --git a/server/i18n/locales/en_us.json b/server/i18n/locales/en_us.json index 715020dd..6275655e 100644 --- a/server/i18n/locales/en_us.json +++ b/server/i18n/locales/en_us.json @@ -547,6 +547,9 @@ "sources": { "create": "Create source", "createDesc": "Drop will use this source to access your game library, and make them available.", + "deleteButton": "Delete source", + "deleteDesc": "Deleting \"{0}\" will cascade delete the library, all of its games, all of their versions, and all of their metadata. This action cannot be undone.", + "deleteTitle": "Delete library source?", "desc": "Configure your library sources, where Drop will look for new games and versions to import.", "documentationLink": "Documentation {arrow}", "edit": "Edit source", diff --git a/server/layouts/admin.vue b/server/layouts/admin.vue index 6a88633f..19087df2 100644 --- a/server/layouts/admin.vue +++ b/server/layouts/admin.vue @@ -126,16 +126,50 @@
- +
+ + +
+ +
    +
  1. + + + + + + + + + + + + + +
  2. + +
+
@@ -156,6 +190,9 @@ import { DialogPanel, TransitionChild, TransitionRoot, + Menu, + MenuButton, + MenuItems, } from "@headlessui/vue"; import { Bars3Icon, @@ -168,7 +205,7 @@ import { } from "@heroicons/vue/24/outline"; import type { NavigationItem } from "~/composables/types"; import { useCurrentNavigationIndex } from "~/composables/current-page-engine"; -import { ArrowLeftIcon } from "@heroicons/vue/16/solid"; +import { ArrowLeftIcon, BellIcon } from "@heroicons/vue/16/solid"; import { XMarkIcon } from "@heroicons/vue/24/solid"; import type { Settings } from "~/server/internal/utils/types"; @@ -219,10 +256,10 @@ const navigation: Array = [ }, ]; -// const notifications = useNotifications(); -// const unreadNotifications = computed(() => -// notifications.value.filter((e) => !e.read) -// ); +const notifications = useNotifications(); +const unreadNotifications = computed(() => + notifications.value.filter((e) => !e.read), +); const currentNavigationIndex = useCurrentNavigationIndex(navigation); diff --git a/server/middleware/require-user.global.ts b/server/middleware/require-user.global.ts index 93084d32..87fba8c9 100644 --- a/server/middleware/require-user.global.ts +++ b/server/middleware/require-user.global.ts @@ -2,14 +2,13 @@ const whitelistedPrefixes = ["/auth", "/api", "/setup"]; const requireAdmin = ["/admin"]; export default defineNuxtRouteMiddleware(async (to, _from) => { - if (import.meta.server) return; const error = useError(); if (error.value !== undefined) return; if (whitelistedPrefixes.findIndex((e) => to.fullPath.startsWith(e)) != -1) return; const user = useUser(); - if (user === undefined) { + if (user.value === undefined) { await updateUser(); } if (!user.value) { diff --git a/server/pages/admin/library/sources/index.vue b/server/pages/admin/library/sources/index.vue index 20fce8a7..9e3878e0 100644 --- a/server/pages/admin/library/sources/index.vue +++ b/server/pages/admin/library/sources/index.vue @@ -347,30 +347,45 @@ function edit(index: number) { actionSourceOpen.value = true; } -async function deleteSource(index: number) { +function deleteSource(index: number) { const source = sources.value[index]; if (!source) return; - try { - await $dropFetch("/api/v1/admin/library/sources", { - method: "DELETE", - body: { id: source.id }, - headers, - }); - } catch (e) { - createModal( - ModalType.Notification, - { - title: t("errors.library.source.delete.title"), - description: t("errors.library.source.delete.desc", [ - // @ts-expect-error attempt to display statusMessage on error - e?.statusMessage ?? t("errors.unknown"), - ]), - }, - (_, c) => c(), - ); - } + createModal( + ModalType.Confirmation, + { + title: t("library.admin.sources.deleteTitle"), + description: t("library.admin.sources.deleteDesc", [source.name]), + buttonText: t("library.admin.sources.deleteButton"), + }, + async (event, close) => { + if (event !== "confirm") return close(); - sources.value.splice(index, 1); + try { + await $dropFetch("/api/v1/admin/library/sources", { + method: "DELETE", + body: { id: source.id }, + headers, + }); + } catch (e) { + createModal( + ModalType.Notification, + { + title: t("errors.library.source.delete.title"), + description: t("errors.library.source.delete.desc", [ + // @ts-expect-error attempt to display statusMessage on error + e?.statusMessage ?? t("errors.unknown"), + ]), + }, + (_, c) => c(), + ); + return close(); + } + + const currentIndex = sources.value.findIndex((s) => s.id === source.id); + if (currentIndex !== -1) sources.value.splice(currentIndex, 1); + close(); + }, + ); }