fix display name minimum and maximum length

This commit is contained in:
Face 2025-06-08 21:36:50 +03:00
parent 52faf16837
commit 5c95051ddf
3 changed files with 57 additions and 9 deletions

View file

@ -8,6 +8,18 @@ import { MAX_FILE_SIZE } from '$lib/data/constants';
import { isNameAppropriate } from '$lib/server/moderation'; import { isNameAppropriate } from '$lib/server/moderation';
async function validateInputs(name: string, bio: string, username: string, avatarFile: File | null) { async function validateInputs(name: string, bio: string, username: string, avatarFile: File | null) {
if (!name || !name.trim()) {
throw error(400, 'Display name is required');
}
if (name.trim().length < 2) {
throw error(400, 'Display name must be at least 2 characters');
}
if (name.trim().length > 50) {
throw error(400, 'Display name must be 50 characters or less');
}
if (name && !(await isNameAppropriate(name.trim()))) { if (name && !(await isNameAppropriate(name.trim()))) {
throw error(400, 'Name contains inappropriate content'); throw error(400, 'Name contains inappropriate content');
} }

View file

@ -7,15 +7,13 @@ import { isNameAppropriate } from '$lib/server/moderation';
export async function GET({ url }) { export async function GET({ url }) {
let username = url.searchParams.get('username')?.toLowerCase().trim(); let username = url.searchParams.get('username')?.toLowerCase().trim();
if (!username) { if (!username) {
return json({ available: false }); return json({ available: false, reason: 'Username is required.' });
} }
username = username.trim().replace(/\s+/g, ' ');
if (username.length < 3 || username.length > 30) { if (username.length < 3 || username.length > 30) {
return json({ return json({
available: false, available: false,
reason: 'Username must be between 3 and 30 characters' reason: 'Username must be 3-30 characters.'
}); });
} }
@ -23,17 +21,29 @@ export async function GET({ url }) {
if (!alphanumericRegex.test(username)) { if (!alphanumericRegex.test(username)) {
return json({ return json({
available: false, available: false,
reason: 'Username must contain only lowercase letters, numbers, and underscores' reason: 'Username can only contain lowercase letters, numbers, and underscores.'
});
}
const purelyNumericRegex = /^\d+$/;
if (purelyNumericRegex.test(username)) {
return json({
available: false,
reason: 'Username cannot be purely numeric.'
}); });
} }
if (!(await isNameAppropriate(username))) { if (!(await isNameAppropriate(username))) {
return json({ available: false, reason: 'Inappropriate content' }); return json({ available: false, reason: 'Username contains inappropriate content.' });
} }
const exists = await db.query.user.findFirst({ const exists = await db.query.user.findFirst({
where: eq(user.username, username) where: eq(user.username, username)
}); });
return json({ available: !exists }); if (exists) {
return json({ available: false, reason: 'Username is already taken.' });
}
return json({ available: true });
} }

View file

@ -27,6 +27,8 @@
let previewUrl: string | null = $state(null); let previewUrl: string | null = $state(null);
let currentAvatarUrl = $derived(previewUrl || getPublicUrl($USER_DATA?.image ?? null)); let currentAvatarUrl = $derived(previewUrl || getPublicUrl($USER_DATA?.image ?? null));
let nameError = $state('');
let isDirty = $derived( let isDirty = $derived(
name !== ($USER_DATA?.name || '') || name !== ($USER_DATA?.name || '') ||
bio !== ($USER_DATA?.bio ?? '') || bio !== ($USER_DATA?.bio ?? '') ||
@ -101,6 +103,22 @@
if (username !== initialUsername) checkUsername(username); if (username !== initialUsername) checkUsername(username);
}); });
$effect(() => {
validateName();
});
function validateName() {
if (!name.trim()) {
nameError = 'Display name is required.';
} else if (name.trim().length < 2) {
nameError = 'Display name must be at least 2 characters.';
} else if (name.trim().length > 50) {
nameError = 'Display name must be 50 characters or less.';
} else {
nameError = '';
}
}
async function handleSubmit(e: Event) { async function handleSubmit(e: Event) {
e.preventDefault(); e.preventDefault();
loading = true; loading = true;
@ -317,7 +335,15 @@
<form onsubmit={handleSubmit} class="space-y-4"> <form onsubmit={handleSubmit} class="space-y-4">
<div class="space-y-2"> <div class="space-y-2">
<Label for="name">Display Name</Label> <Label for="name">Display Name</Label>
<Input id="name" bind:value={name} required /> <Input
id="name"
bind:value={name}
required
class={nameError ? 'border-destructive' : ''}
/>
{#if nameError}
<p class="text-destructive text-sm">{nameError}</p>
{/if}
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
@ -355,7 +381,7 @@
<Textarea id="bio" bind:value={bio} rows={4} placeholder="Tell us about yourself" /> <Textarea id="bio" bind:value={bio} rows={4} placeholder="Tell us about yourself" />
</div> </div>
<Button type="submit" disabled={loading || !isDirty}> <Button type="submit" disabled={loading || !isDirty || !!nameError}>
{loading ? 'Saving…' : 'Save Changes'} {loading ? 'Saving…' : 'Save Changes'}
</Button> </Button>
</form> </form>