From d11ee72527e2250e674af9674578b403b06b2873 Mon Sep 17 00:00:00 2001 From: Robert Helewka Date: Fri, 8 May 2026 06:55:07 -0400 Subject: [PATCH] feat(library): protect Daedalus workspace-scoped libraries from manual deletion - Add guard in `library_delete` view to block deletion of libraries owned by a Daedalus workspace, redirecting with an error message - Disable the Delete button in `library_detail.html` for workspace- scoped libraries and show a warning alert explaining managed ownership - Add a "Daedalus workspace" badge in both `library_detail.html` and `library_list.html` to visually identify workspace-owned libraries Prevents state desync between Mnemosyne and Daedalus by ensuring workspace-scoped libraries can only be removed via the Daedalus workspace DELETE API endpoint. --- .../templates/library/library_detail.html | 31 ++++++++++++++++++- .../templates/library/library_list.html | 9 +++++- mnemosyne/library/views.py | 11 +++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/mnemosyne/library/templates/library/library_detail.html b/mnemosyne/library/templates/library/library_detail.html index 1991d39..b2ea2f5 100644 --- a/mnemosyne/library/templates/library/library_detail.html +++ b/mnemosyne/library/templates/library/library_detail.html @@ -10,17 +10,46 @@

{{ library.name }}

-
{{ library.library_type }}
+
+
{{ library.library_type }}
+ {% if library.workspace_id %} +
+ Daedalus workspace +
+ {% endif %} +
{% if library.description %}

{{ library.description }}

{% endif %}
Edit + {% if library.workspace_id %} + + {% else %} Delete + {% endif %}
+{% if library.workspace_id %} +
+
+
Managed by Daedalus
+
+ This library was created for Daedalus workspace + {{ library.workspace_id }}. + Items here are owned by the workspace; deleting the workspace in + Daedalus will remove this library. Do not delete it manually. +
+
+
+{% endif %} +
diff --git a/mnemosyne/library/templates/library/library_list.html b/mnemosyne/library/templates/library/library_list.html index 1e25e94..1f28008 100644 --- a/mnemosyne/library/templates/library/library_list.html +++ b/mnemosyne/library/templates/library/library_list.html @@ -31,7 +31,14 @@ {{ lib.name }} -
{{ lib.library_type }}
+
+
{{ lib.library_type }}
+ {% if lib.workspace_id %} +
+ Daedalus workspace +
+ {% endif %} +
{% if lib.description %}

{{ lib.description|truncatewords:20 }}

{% endif %} diff --git a/mnemosyne/library/views.py b/mnemosyne/library/views.py index e57a406..2e36a17 100644 --- a/mnemosyne/library/views.py +++ b/mnemosyne/library/views.py @@ -299,6 +299,17 @@ def library_delete(request, uid): messages.error(request, f"Library not found: {e}") return redirect("library:library-list") + # Daedalus owns the lifecycle of workspace-scoped libraries — they can + # only be deleted via DELETE /library/api/workspaces/{workspace_id}/. + # Block the human delete path so a stray click can't desync state. + if lib.workspace_id: + messages.error( + request, + f'"{lib.name}" is managed by Daedalus workspace ' + f"{lib.workspace_id}. Delete it from Daedalus, not here.", + ) + return redirect("library:library-detail", uid=uid) + if request.method == "POST": name = lib.name lib.delete()