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) => {
const user = await tx.addUser({
firstname: input.firstname,

View File

@@ -22,7 +22,7 @@
function onBlockedUserDelete(blockedUser: BlockedUser) {
$confirmPopupState = {
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)
};
}

View File

@@ -24,7 +24,7 @@
submitButtonTitle: 'Erstellen',
confirmPopupTitle: 'Nutzer blockieren',
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}
keys={[

View File

@@ -1,6 +1,6 @@
<script lang="ts">
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 { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
@@ -14,6 +14,14 @@
});
// 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) {
$confirmPopupState = {
title: 'Nutzer löschen?',
@@ -27,6 +35,14 @@
<span>{value.charAt(0).toUpperCase() + value.slice(1)}</span>
{/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
data={users}
count={true}
@@ -39,6 +55,7 @@
{ key: 'edition', label: 'Edition', width: 5, sortable: true, transform: edition },
{ key: 'uuid', label: 'UUID', width: 23 }
]}
extraActions={blockAction}
onEdit={(user) => (editPopupUser = user)}
onDelete={onUserDelete}
/>

View File

@@ -58,6 +58,16 @@ export async function editUser(user: User) {
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) {
const { error } = await actions.user.deleteUser({ id: user.id });
if (error) {

View File

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

View File

@@ -4,7 +4,12 @@ import { db } from '@db/database.ts';
export const GET = externalApi({
auth: true,
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 });
const strikes = await db.getStrikesByUserId({ userId: user.id });