From c26203db9f530abea4e6ef0698fc824c40bd5766 Mon Sep 17 00:00:00 2001 From: Francois Ribemont Date: Sun, 8 Jun 2025 02:48:11 +0100 Subject: [PATCH 1/3] #86 Adds delete user functionality in admin panel --- components/DeleteUserModal.vue | 75 +++++++++++++++++++ composables/users.ts | 42 +++++++++++ i18n/locales/en_us.json | 12 +++ pages/admin/users/index.vue | 23 +++++- .../migration.sql | 41 ++++++++++ prisma/models/auth.prisma | 6 +- prisma/models/client.prisma | 2 +- prisma/models/collection.prisma | 2 +- prisma/models/news.prisma | 2 +- prisma/models/user.prisma | 2 +- .../api/v1/admin/users/[id]/index.delete.ts | 31 ++++++++ server/internal/acls/descriptions.ts | 1 + server/internal/acls/index.ts | 1 + 13 files changed, 232 insertions(+), 8 deletions(-) create mode 100644 components/DeleteUserModal.vue create mode 100644 composables/users.ts create mode 100644 prisma/migrations/20250608010030_delete_user_cascade/migration.sql create mode 100644 server/api/v1/admin/users/[id]/index.delete.ts diff --git a/components/DeleteUserModal.vue b/components/DeleteUserModal.vue new file mode 100644 index 00000000..ce780060 --- /dev/null +++ b/components/DeleteUserModal.vue @@ -0,0 +1,75 @@ + + + diff --git a/composables/users.ts b/composables/users.ts new file mode 100644 index 00000000..58a10936 --- /dev/null +++ b/composables/users.ts @@ -0,0 +1,42 @@ +import type { SerializeObject } from "nitropack"; +import type { User, AuthMec } from "~/prisma/client"; + +export const useUsers = () => + useState< + | Array< + SerializeObject< + User & { + authMecs?: Array<{ id: string; mec: AuthMec }>; + } + > + > + | undefined + >("users", () => undefined); + +export const fetchUsers = async (options?: { + limit?: number; + skip?: number; + orderBy?: "asc" | "desc"; + tags?: string[]; + search?: string; +}) => { + const query = new URLSearchParams(); + + if (options?.limit) query.set("limit", options.limit.toString()); + if (options?.skip) query.set("skip", options.skip.toString()); + if (options?.orderBy) query.set("order", options.orderBy); + if (options?.tags?.length) query.set("tags", options.tags.join(",")); + if (options?.search) query.set("search", options.search); + + const users = useUsers(); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore forget why this ignor exists + const newValue = await $dropFetch( + `/api/v1/admin/users?${query.toString()}`, + ); + + users.value = newValue; + + return newValue; +}; diff --git a/i18n/locales/en_us.json b/i18n/locales/en_us.json index a9bfdf66..c7f9bda2 100644 --- a/i18n/locales/en_us.json +++ b/i18n/locales/en_us.json @@ -118,6 +118,14 @@ "invalidInvite": "Invalid or expired invitation", "usernameTaken": "Username already taken." }, + "admin": { + "user": { + "delete": { + "desc": "Drop couldn't delete this user: {0}", + "title": "Failed to delete user" + } + } + }, "backHome": "{arrow} Back to home", "invalidBody": "Invalid request body: {0}", "inviteRequired": "Invitation required to sign up.", @@ -238,6 +246,10 @@ "srEditLabel": "Edit", "adminUserLabel": "Admin user", "normalUserLabel": "Normal user", + + "delete": "Delete", + "deleteUser": "Delete user {0}", + "authentication": { "title": "Authentication", "description": "Drop supports a variety of \"authentication mechanisms\". As you enable or disable them, they are shown on the sign in screen for users to select from. Click the dot menu to configure the authentication mechanism.", diff --git a/pages/admin/users/index.vue b/pages/admin/users/index.vue index 3c84c857..3fb0901e 100644 --- a/pages/admin/users/index.vue +++ b/pages/admin/users/index.vue @@ -115,6 +115,15 @@ + + + +