allow blocked users to sign up, block server join instead
All checks were successful
deploy / build-and-deploy (push) Successful in 19s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 19:52:30 +02:00
parent 1d25e0ed59
commit 31ad92a6e2
7 changed files with 44 additions and 19 deletions

View File

@@ -62,17 +62,6 @@ export const signup = {
} }
} }
// check if user is blocked
if (uuid) {
const blockedUser = await db.getBlockedUserByUuid({ uuid: uuid });
if (blockedUser) {
throw new ActionError({
code: 'FORBIDDEN',
message: 'Du bist für die Registrierung gesperrt'
});
}
}
await db.transaction(async (tx) => { await db.transaction(async (tx) => {
const user = await tx.addUser({ const user = await tx.addUser({
firstname: input.firstname, firstname: input.firstname,

View File

@@ -22,7 +22,7 @@
function onBlockedUserDelete(blockedUser: BlockedUser) { function onBlockedUserDelete(blockedUser: BlockedUser) {
$confirmPopupState = { $confirmPopupState = {
title: 'Nutzer entblockieren?', title: 'Nutzer entblockieren?',
message: 'Soll der Nutzer wirklich entblockiert werden?\nDieser kann sich danach wieder registrieren.', message: 'Soll der Nutzer wirklich entblockiert werden?\nDieser kann danach dem Server wieder beitreten.',
onConfirm: () => deleteBlockedUser(blockedUser) onConfirm: () => deleteBlockedUser(blockedUser)
}; };
} }

View File

@@ -24,7 +24,7 @@
submitButtonTitle: 'Erstellen', submitButtonTitle: 'Erstellen',
confirmPopupTitle: 'Nutzer blockieren', confirmPopupTitle: 'Nutzer blockieren',
confirmPopupMessage: confirmPopupMessage:
'Bist du sicher, dass der Nutzer blockiert werden soll?\nEin blockierter Nutzer kann sich nicht mehr registrieren.' 'Bist du sicher, dass der Nutzer blockiert werden soll?\nEin blockierter Nutzer kann dem Server nicht mehr beitreten.'
}} }}
target={null} target={null}
keys={[ keys={[

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import DataTable from '@components/admin/table/DataTable.svelte'; import DataTable from '@components/admin/table/DataTable.svelte';
import { deleteUser, editUser, type User, users } from '@app/admin/users/users.ts'; import { blockUser, deleteUser, editUser, type User, users } from '@app/admin/users/users.ts';
import CrudPopup from '@components/admin/popup/CrudPopup.svelte'; import CrudPopup from '@components/admin/popup/CrudPopup.svelte';
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts'; import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
@@ -14,6 +14,14 @@
}); });
// callback // callback
function onUserBlock(user: User) {
$confirmPopupState = {
title: 'Nutzer sperren?',
message: 'Soll der Nutzer wirklich gesperrt werden?\nEin gesperrter Nutzer kann dem Server nicht mehr beitreten.',
onConfirm: () => blockUser(user)
};
}
function onUserDelete(user: User) { function onUserDelete(user: User) {
$confirmPopupState = { $confirmPopupState = {
title: 'Nutzer löschen?', title: 'Nutzer löschen?',
@@ -27,6 +35,14 @@
<span>{value.charAt(0).toUpperCase() + value.slice(1)}</span> <span>{value.charAt(0).toUpperCase() + value.slice(1)}</span>
{/snippet} {/snippet}
{#snippet blockAction(user: User)}
{#if user.uuid}
<button class="cursor-pointer" onclick={() => onUserBlock(user)}>
<span class="iconify iconify-[heroicons--no-symbol]"></span>
</button>
{/if}
{/snippet}
<DataTable <DataTable
data={users} data={users}
count={true} count={true}
@@ -39,6 +55,7 @@
{ key: 'edition', label: 'Edition', width: 5, sortable: true, transform: edition }, { key: 'edition', label: 'Edition', width: 5, sortable: true, transform: edition },
{ key: 'uuid', label: 'UUID', width: 23 } { key: 'uuid', label: 'UUID', width: 23 }
]} ]}
extraActions={blockAction}
onEdit={(user) => (editPopupUser = user)} onEdit={(user) => (editPopupUser = user)}
onDelete={onUserDelete} onDelete={onUserDelete}
/> />

View File

@@ -58,6 +58,16 @@ export async function editUser(user: User) {
updateWritableArray(users, user, (t) => t.id == user.id); updateWritableArray(users, user, (t) => t.id == user.id);
} }
export async function blockUser(user: User) {
if (!user.uuid) return;
const { error } = await actions.user.addBlocked({ uuid: user.uuid, comment: null });
if (error) {
actionErrorPopup(error);
return;
}
}
export async function deleteUser(user: User) { export async function deleteUser(user: User) {
const { error } = await actions.user.deleteUser({ id: user.id }); const { error } = await actions.user.deleteUser({ id: user.id });
if (error) { if (error) {

View File

@@ -21,10 +21,11 @@
onClick?: (t: T) => void; onClick?: (t: T) => void;
onEdit?: (t: T) => void; onEdit?: (t: T) => void;
onDelete?: (t: T) => void; onDelete?: (t: T) => void;
extraActions?: Snippet<[T]>;
} }
// input // input
let { data, count, keys, onClick, onEdit, onDelete }: Props<any> = $props(); let { data, count, keys, onClick, onEdit, onDelete, extraActions }: Props<any> = $props();
</script> </script>
<div class="max-h-screen overflow-x-auto"> <div class="max-h-screen overflow-x-auto">
@@ -39,7 +40,7 @@
>{key.label}</SortableTh >{key.label}</SortableTh
> >
{/each} {/each}
{#if onEdit || onDelete} {#if onEdit || onDelete || extraActions}
<SortableTh style="width: 5%"></SortableTh> <SortableTh style="width: 5%"></SortableTh>
{/if} {/if}
</SortableTr> </SortableTr>
@@ -59,8 +60,11 @@
{/if} {/if}
</td> </td>
{/each} {/each}
{#if onEdit || onDelete} {#if onEdit || onDelete || extraActions}
<td class="px-3"> <td class="px-3 whitespace-nowrap">
{#if extraActions}
{@render extraActions(d)}
{/if}
{#if onEdit} {#if onEdit}
<button class="cursor-pointer" onclick={() => onEdit(d)}> <button class="cursor-pointer" onclick={() => onEdit(d)}>
<span class="iconify iconify-[heroicons--pencil-square]"></span> <span class="iconify iconify-[heroicons--pencil-square]"></span>

View File

@@ -4,7 +4,12 @@ import { db } from '@db/database.ts';
export const GET = externalApi({ export const GET = externalApi({
auth: true, auth: true,
handler: async ({ params }) => { handler: async ({ params }) => {
const user = await db.getUserByUuid({ uuid: params['uuid']! }); const uuid = params['uuid']!;
const blockedUser = await db.getBlockedUserByUuid({ uuid });
if (blockedUser) return new Response(null, { status: 403 });
const user = await db.getUserByUuid({ uuid });
if (!user) return new Response(null, { status: 404 }); if (!user) return new Response(null, { status: 404 });
const strikes = await db.getStrikesByUserId({ userId: user.id }); const strikes = await db.getStrikesByUserId({ userId: user.id });