feat(auth): add username field to user schema and authentication process
All checks were successful
Build Docker Image / build (push) Successful in 1m12s

- Updated user schema to include a unique username field.
- Modified authentication logic to support sign-in using either email or username.
- Enhanced sign-up process to require a username and validate its uniqueness.
- Updated login and profile routes to reflect changes in user identification.
- Adjusted frontend forms to accommodate username input alongside email.
This commit is contained in:
2026-03-06 20:16:05 +01:00
parent ce08329b2d
commit 249da5ad2e
9 changed files with 1447 additions and 32 deletions

View File

@@ -2,7 +2,10 @@ import { fail, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
import type { PageServerLoad } from './$types';
import { auth } from '$lib/server/auth';
import { db } from '$lib/server/db';
import { user } from '$lib/server/db/schema';
import { APIError } from 'better-auth/api';
import { sql } from 'drizzle-orm';
export const load: PageServerLoad = async (event) => {
if (event.locals.user) {
@@ -14,9 +17,28 @@ export const load: PageServerLoad = async (event) => {
export const actions: Actions = {
signInEmail: async (event) => {
const formData = await event.request.formData();
const email = formData.get('email')?.toString() ?? '';
const identifier = formData.get('identifier')?.toString().trim() ?? formData.get('email')?.toString().trim() ?? '';
const password = formData.get('password')?.toString() ?? '';
if (!identifier) {
return fail(400, { message: 'Email ou nom d\'utilisateur requis' });
}
let email = identifier;
if (!identifier.includes('@')) {
const [foundUser] = await db
.select({ email: user.email })
.from(user)
.where(sql`lower(${user.username}) = ${identifier.toLowerCase()}`)
.limit(1);
if (!foundUser) {
return fail(400, { message: 'Identifiants invalides' });
}
email = foundUser.email;
}
try {
await auth.api.signInEmail({
body: {
@@ -38,7 +60,33 @@ export const actions: Actions = {
const formData = await event.request.formData();
const email = formData.get('email')?.toString() ?? '';
const password = formData.get('password')?.toString() ?? '';
const confirmPassword = formData.get('confirmPassword')?.toString() ?? '';
const name = formData.get('name')?.toString() ?? '';
const username = formData.get('username')?.toString().trim() ?? '';
if (!username) {
return fail(400, { message: 'Nom d\'utilisateur requis' });
}
if (!/^[a-zA-Z0-9._-]{3,30}$/.test(username)) {
return fail(400, {
message: "Le nom d'utilisateur doit contenir 3 à 30 caractères (lettres, chiffres, ., _, -)"
});
}
const [existingUsername] = await db
.select({ id: user.id })
.from(user)
.where(sql`lower(${user.username}) = ${username.toLowerCase()}`)
.limit(1);
if (existingUsername) {
return fail(400, { message: "Ce nom d'utilisateur est déjà pris" });
}
if (password !== confirmPassword) {
return fail(400, { message: 'Les mots de passe ne correspondent pas' });
}
try {
await auth.api.signUpEmail({
@@ -46,6 +94,7 @@ export const actions: Actions = {
email,
password,
name,
username,
callbackURL: '/auth/verification-success'
}
});