diff --git a/app/components/admin/UserRoles.vue b/app/components/admin/UserRoles.vue new file mode 100644 index 0000000..5c4d19f --- /dev/null +++ b/app/components/admin/UserRoles.vue @@ -0,0 +1,190 @@ + + + diff --git a/app/lang/de.json b/app/lang/de.json index 7d6c4e1..77b8b65 100644 --- a/app/lang/de.json +++ b/app/lang/de.json @@ -41,6 +41,11 @@ "TEXT": "Freitext", "WORD": "Ein Wort", "CHAR": "Ein Zeichen" + }, + "user_role": { + "ADMIN": "Admin", + "CREATOR": "Ersteller", + "USER": "Spieler" } }, "error": { @@ -131,10 +136,20 @@ "filter_quest": "Filtere Bilder nach Quests" }, "profile": { + "admins_and_creators": "Admins & Ersteller", "new_password": "Neues Passwort", "no_pictures": "Noch keine Bilder hochgeladen.", "old_password": "Altes Passwort", - "uploaded_pictures": "Hochgeladene Bilder" + "own_role_hint": "Du kannst deine eigene Rolle nicht ändern.", + "promote": "Befördern", + "promote_user": "Benutzer befördern", + "role": "Rolle", + "role_changed": "Rolle aktualisiert!", + "role_change_error": "Rolle konnte nicht geändert werden!", + "role_confirm": "Möchtest du die Rolle von {name} zu {role} ändern?", + "search_user": "Benutzer per E-Mail suchen...", + "uploaded_pictures": "Hochgeladene Bilder", + "user_management": "Benutzerverwaltung" }, "quest": { "already_finalized": "Antwort ist bereits finalisiert, keine Änderung erlaubt!", diff --git a/app/lang/en.json b/app/lang/en.json index 7618ab5..2d3a933 100644 --- a/app/lang/en.json +++ b/app/lang/en.json @@ -41,6 +41,11 @@ "TEXT": "Free text", "WORD": "One word", "CHAR": "One character" + }, + "user_role": { + "ADMIN": "Admin", + "CREATOR": "Creator", + "USER": "Player" } }, "error": { @@ -131,10 +136,20 @@ "filter_quest": "Filter pictures by quests" }, "profile": { + "admins_and_creators": "Admins & Creators", "new_password": "New password", "no_pictures": "No pictures uploaded yet.", "old_password": "Old password", - "uploaded_pictures": "Uploaded pictures" + "own_role_hint": "You cannot change your own role.", + "promote": "Promote", + "promote_user": "Promote a user", + "role": "Role", + "role_changed": "Role updated!", + "role_change_error": "Could not change role!", + "role_confirm": "Do you want to change the role of {name} to {role}?", + "search_user": "Search user by email...", + "uploaded_pictures": "Uploaded pictures", + "user_management": "User management" }, "quest": { "already_finalized": "Answer is already finalized, no update allowed!", diff --git a/app/pages/profile.vue b/app/pages/profile.vue index 051168d..3428ff0 100644 --- a/app/pages/profile.vue +++ b/app/pages/profile.vue @@ -266,6 +266,7 @@ async function submitPassword() { {{ t('page.profile.no_pictures') }} + diff --git a/server/api/admin/user/[userId]/role.post.ts b/server/api/admin/user/[userId]/role.post.ts new file mode 100644 index 0000000..5eb32a2 --- /dev/null +++ b/server/api/admin/user/[userId]/role.post.ts @@ -0,0 +1,53 @@ +import { useValidatedBody, useValidatedParams, z, zh } from 'h3-zod'; +import prisma from '~~/lib/prisma'; + +export default defineEventHandler(async (event) => { + const user = await requireUserSession(event); + + if (user.user.role !== 'ADMIN') { + throw createError({ + status: 403, + statusText: 'Not allowed!' + }); + } + + const { userId } = await useValidatedParams( + event, + z.object({ + userId: zh.numAsString + }) + ); + + const { role } = await useValidatedBody( + event, + z.object({ + role: z.enum(['USER', 'CREATOR', 'ADMIN']) + }) + ); + + if (userId === user.user.id) { + throw createError({ + status: 400, + statusText: 'You cannot change your own role!' + }); + } + + const updated = await prisma.user.update({ + where: { + id: userId, + deactivated: false, + deletedAt: null + }, + data: { + role + }, + select: { + id: true, + name: true, + email: true, + role: true + } + }); + + return { success: true, user: updated }; +}); diff --git a/server/api/admin/user/index.get.ts b/server/api/admin/user/index.get.ts new file mode 100644 index 0000000..07c6842 --- /dev/null +++ b/server/api/admin/user/index.get.ts @@ -0,0 +1,31 @@ +import prisma from '~~/lib/prisma'; + +export default defineEventHandler(async (event) => { + const user = await requireUserSession(event); + + if (user.user.role !== 'ADMIN') { + throw createError({ + status: 403, + statusText: 'Not allowed!' + }); + } + + return prisma.user.findMany({ + where: { + role: { + in: ['ADMIN', 'CREATOR'] + }, + deactivated: false, + deletedAt: null + }, + select: { + id: true, + name: true, + email: true, + role: true + }, + orderBy: { + createdAt: 'asc' + } + }); +}); diff --git a/server/api/admin/user/search.get.ts b/server/api/admin/user/search.get.ts new file mode 100644 index 0000000..63ae169 --- /dev/null +++ b/server/api/admin/user/search.get.ts @@ -0,0 +1,41 @@ +import { useValidatedQuery, z } from 'h3-zod'; +import prisma from '~~/lib/prisma'; + +export default defineEventHandler(async (event) => { + const user = await requireUserSession(event); + + if (user.user.role !== 'ADMIN') { + throw createError({ + status: 403, + statusText: 'Not allowed!' + }); + } + + const { email } = await useValidatedQuery( + event, + z.object({ + email: z.string().trim().default('') + }) + ); + + return prisma.user.findMany({ + where: { + role: 'USER', + deactivated: false, + deletedAt: null, + email: { + contains: email + } + }, + select: { + id: true, + name: true, + email: true, + role: true + }, + take: 10, + orderBy: { + createdAt: 'desc' + } + }); +});