mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:41:09 +02:00
🐛 fix(auth): require name field in registration forms
Add required name field (min 2 chars) to all registration forms to fix Better Auth validation error. Updates backend DTO, shared-auth service, shared-auth-ui RegisterPage component, i18n translations, and all app auth stores and register pages.
This commit is contained in:
parent
11324b5e68
commit
d3e11b320a
28 changed files with 151 additions and 56 deletions
|
|
@ -111,16 +111,16 @@ export const authStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
* Sign up with email, password, and name
|
||||
*/
|
||||
async signUp(email: string, password: string) {
|
||||
async signUp(email: string, password: string, name: string) {
|
||||
const authService = await getAuthService();
|
||||
if (!authService) {
|
||||
return { success: false, error: 'Auth not available on server', needsVerification: false };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await authService.signUp(email, password);
|
||||
const result = await authService.signUp(email, password, name);
|
||||
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || 'Signup failed', needsVerification: false };
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@
|
|||
// Get translations based on current locale
|
||||
const translations = $derived(getRegisterTranslations($locale || 'de'));
|
||||
|
||||
async function handleSignUp(email: string, password: string) {
|
||||
return authStore.signUp(email, password);
|
||||
async function handleSignUp(email: string, password: string, name: string) {
|
||||
return authStore.signUp(email, password, name);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -110,16 +110,16 @@ export const authStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
* Sign up with email, password, and name
|
||||
*/
|
||||
async signUp(email: string, password: string) {
|
||||
async signUp(email: string, password: string, name: string) {
|
||||
const authService = await getAuthService();
|
||||
if (!authService) {
|
||||
return { success: false, error: 'Auth not available on server', needsVerification: false };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await authService.signUp(email, password);
|
||||
const result = await authService.signUp(email, password, name);
|
||||
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || 'Signup failed', needsVerification: false };
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@
|
|||
// Get translations based on current locale
|
||||
const translations = $derived(getRegisterTranslations($locale || 'de'));
|
||||
|
||||
async function handleSignUp(email: string, password: string) {
|
||||
return authStore.signUp(email, password);
|
||||
async function handleSignUp(email: string, password: string, name: string) {
|
||||
return authStore.signUp(email, password, name);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -110,16 +110,16 @@ export const authStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
* Sign up with email, password, and name
|
||||
*/
|
||||
async signUp(email: string, password: string) {
|
||||
async signUp(email: string, password: string, name: string) {
|
||||
const authService = await getAuthService();
|
||||
if (!authService) {
|
||||
return { success: false, error: 'Auth not available on server', needsVerification: false };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await authService.signUp(email, password);
|
||||
const result = await authService.signUp(email, password, name);
|
||||
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || 'Signup failed', needsVerification: false };
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
// Get translations based on current locale
|
||||
const translations = $derived(getRegisterTranslations($locale || 'de'));
|
||||
|
||||
async function handleSignUp(email: string, password: string) {
|
||||
return authStore.signUp(email, password);
|
||||
async function handleSignUp(email: string, password: string, name: string) {
|
||||
return authStore.signUp(email, password, name);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -110,16 +110,16 @@ export const authStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
* Sign up with email, password, and name
|
||||
*/
|
||||
async signUp(email: string, password: string) {
|
||||
async signUp(email: string, password: string, name: string) {
|
||||
const authService = await getAuthService();
|
||||
if (!authService) {
|
||||
return { success: false, error: 'Auth not available on server', needsVerification: false };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await authService.signUp(email, password);
|
||||
const result = await authService.signUp(email, password, name);
|
||||
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || 'Signup failed', needsVerification: false };
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
const translations = $derived(getRegisterTranslations($locale || 'de'));
|
||||
|
||||
async function handleSignUp(email: string, password: string) {
|
||||
return authStore.signUp(email, password);
|
||||
async function handleSignUp(email: string, password: string, name: string) {
|
||||
return authStore.signUp(email, password, name);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -116,19 +116,20 @@ export const authStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
* Sign up with email, password and name
|
||||
* @param email User email
|
||||
* @param password User password
|
||||
* @param name User's display name
|
||||
* @param referralCode Optional referral code for bonus credits
|
||||
*/
|
||||
async signUp(email: string, password: string, referralCode?: string) {
|
||||
async signUp(email: string, password: string, name: string, referralCode?: string) {
|
||||
const authService = await getAuthService();
|
||||
if (!authService) {
|
||||
return { success: false, error: 'Auth not available on server', needsVerification: false };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await authService.signUp(email, password, referralCode);
|
||||
const result = await authService.signUp(email, password, name, referralCode);
|
||||
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || 'Signup failed', needsVerification: false };
|
||||
|
|
|
|||
|
|
@ -9,8 +9,13 @@
|
|||
// Get referral code from URL if present
|
||||
let initialReferralCode = $derived($page.url.searchParams.get('ref') || '');
|
||||
|
||||
async function handleSignUp(email: string, password: string, referralCode?: string) {
|
||||
return authStore.signUp(email, password, referralCode);
|
||||
async function handleSignUp(
|
||||
email: string,
|
||||
password: string,
|
||||
name: string,
|
||||
referralCode?: string
|
||||
) {
|
||||
return authStore.signUp(email, password, name, referralCode);
|
||||
}
|
||||
|
||||
async function handleValidateReferralCode(code: string) {
|
||||
|
|
|
|||
|
|
@ -92,10 +92,10 @@ export const authStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
* Sign up with email, password, and name
|
||||
*/
|
||||
async signUp(email: string, password: string) {
|
||||
const result = await authService.signUp(email, password);
|
||||
async signUp(email: string, password: string, name: string) {
|
||||
const result = await authService.signUp(email, password, name);
|
||||
if (result.success && !result.needsVerification) {
|
||||
const userData = await authService.getUserFromToken();
|
||||
user = toManaUser(userData);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
import AppSlider from '$lib/components/AppSlider.svelte';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
|
||||
async function handleSignUp(email: string, password: string) {
|
||||
return authStore.signUp(email, password);
|
||||
async function handleSignUp(email: string, password: string, name: string) {
|
||||
return authStore.signUp(email, password, name);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ export const authStore = {
|
|||
}
|
||||
},
|
||||
|
||||
async signUp(email: string, password: string): Promise<AuthResult> {
|
||||
async signUp(email: string, password: string, name: string): Promise<AuthResult> {
|
||||
const authService = await getAuthService();
|
||||
if (!authService) {
|
||||
return { success: false, error: 'Auth service not available' };
|
||||
|
|
@ -123,7 +123,7 @@ export const authStore = {
|
|||
|
||||
try {
|
||||
loading = true;
|
||||
const result = await authService.signUp(email, password);
|
||||
const result = await authService.signUp(email, password, name);
|
||||
|
||||
if (result.success) {
|
||||
// Auto-login after signup
|
||||
|
|
|
|||
|
|
@ -143,16 +143,16 @@ export const authStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
* Sign up with email, password, and name
|
||||
*/
|
||||
async signUp(email: string, password: string) {
|
||||
async signUp(email: string, password: string, name: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) {
|
||||
return { success: false, error: 'Auth not available on server', needsVerification: false };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await authService.signUp(email, password);
|
||||
const result = await authService.signUp(email, password, name);
|
||||
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || 'Signup failed', needsVerification: false };
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
// Get translations based on current locale
|
||||
const translations = $derived(getRegisterTranslations($locale || 'de'));
|
||||
|
||||
async function handleSignUp(email: string, password: string) {
|
||||
return authStore.signUp(email, password);
|
||||
async function handleSignUp(email: string, password: string, name: string) {
|
||||
return authStore.signUp(email, password, name);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -131,16 +131,16 @@ export const authStore = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
* Sign up with email, password, and name
|
||||
*/
|
||||
async signUp(email: string, password: string) {
|
||||
async signUp(email: string, password: string, name: string) {
|
||||
const authService = getAuthService();
|
||||
if (!authService) {
|
||||
return { success: false, error: 'Auth not available on server', needsVerification: false };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await authService.signUp(email, password);
|
||||
const result = await authService.signUp(email, password, name);
|
||||
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || 'Signup failed', needsVerification: false };
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
const translations = $derived(getRegisterTranslations($locale || 'de'));
|
||||
|
||||
async function handleSignUp(email: string, password: string) {
|
||||
return authStore.signUp(email, password);
|
||||
async function handleSignUp(email: string, password: string, name: string) {
|
||||
return authStore.signUp(email, password, name);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
const dispatch = createEventDispatcher();
|
||||
|
||||
// Formular-Zustände
|
||||
let name = '';
|
||||
let email = '';
|
||||
let password = '';
|
||||
let confirmPassword = '';
|
||||
|
|
@ -16,11 +17,16 @@
|
|||
// Formular absenden
|
||||
async function handleSubmit() {
|
||||
// Validierung
|
||||
if (!email || !password || !confirmPassword) {
|
||||
if (!name || !email || !password || !confirmPassword) {
|
||||
errorMessage = 'Bitte fülle alle Felder aus.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.length < 2) {
|
||||
errorMessage = 'Der Name muss mindestens 2 Zeichen lang sein.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
errorMessage = 'Die Passwörter stimmen nicht überein.';
|
||||
return;
|
||||
|
|
@ -35,12 +41,13 @@
|
|||
isLoading = true;
|
||||
errorMessage = '';
|
||||
|
||||
const success = await AuthService.register(email, password);
|
||||
const success = await AuthService.register(email, password, name);
|
||||
|
||||
if (success) {
|
||||
successMessage =
|
||||
'Registrierung erfolgreich! Bitte überprüfe deine E-Mails, um dein Konto zu bestätigen.';
|
||||
// Formular zurücksetzen
|
||||
name = '';
|
||||
email = '';
|
||||
password = '';
|
||||
confirmPassword = '';
|
||||
|
|
@ -83,6 +90,19 @@
|
|||
{/if}
|
||||
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
bind:value={name}
|
||||
placeholder="Dein Name"
|
||||
disabled={isLoading}
|
||||
required
|
||||
minlength="2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">E-Mail</label>
|
||||
<input
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
/** Translation strings for the register page */
|
||||
export interface RegisterTranslations {
|
||||
title: string;
|
||||
namePlaceholder: string;
|
||||
nameRequired: string;
|
||||
nameTooShort: string;
|
||||
emailPlaceholder: string;
|
||||
passwordPlaceholder: string;
|
||||
confirmPasswordPlaceholder: string;
|
||||
|
|
@ -46,6 +49,9 @@
|
|||
/** Default English translations */
|
||||
const defaultTranslations: RegisterTranslations = {
|
||||
title: 'Create Account',
|
||||
namePlaceholder: 'Name',
|
||||
nameRequired: 'Name is required',
|
||||
nameTooShort: 'Name must be at least 2 characters',
|
||||
emailPlaceholder: 'Email',
|
||||
passwordPlaceholder: 'Password',
|
||||
confirmPasswordPlaceholder: 'Confirm Password',
|
||||
|
|
@ -81,8 +87,13 @@
|
|||
logo: Component<{ size?: number; color?: string }>;
|
||||
/** Primary color (hex) */
|
||||
primaryColor: string;
|
||||
/** Sign up function (with optional referral code) */
|
||||
onSignUp: (email: string, password: string, referralCode?: string) => Promise<AuthResult>;
|
||||
/** Sign up function (with name and optional referral code) */
|
||||
onSignUp: (
|
||||
email: string,
|
||||
password: string,
|
||||
name: string,
|
||||
referralCode?: string
|
||||
) => Promise<AuthResult>;
|
||||
/** Navigation function */
|
||||
goto: (path: string) => void;
|
||||
/** Success redirect path */
|
||||
|
|
@ -132,6 +143,7 @@
|
|||
let error = $state<string | null>(null);
|
||||
let success = $state(false);
|
||||
let needsVerification = $state(false);
|
||||
let name = $state('');
|
||||
let email = $state('');
|
||||
let password = $state('');
|
||||
let confirmPassword = $state('');
|
||||
|
|
@ -249,6 +261,18 @@
|
|||
success = false;
|
||||
|
||||
// Validation
|
||||
if (!name) {
|
||||
error = t.nameRequired;
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.length < 2) {
|
||||
error = t.nameTooShort;
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
error = t.emailRequired;
|
||||
loading = false;
|
||||
|
|
@ -293,7 +317,7 @@
|
|||
|
||||
// Pass referral code if valid
|
||||
const validReferralCode = referralValidation?.valid ? referralCode : undefined;
|
||||
const result = await onSignUp(email, password, validReferralCode);
|
||||
const result = await onSignUp(email, password, name, validReferralCode);
|
||||
|
||||
loading = false;
|
||||
|
||||
|
|
@ -301,6 +325,7 @@
|
|||
if (result.needsVerification) {
|
||||
needsVerification = true;
|
||||
success = true;
|
||||
name = '';
|
||||
password = '';
|
||||
confirmPassword = '';
|
||||
} else {
|
||||
|
|
@ -407,6 +432,24 @@
|
|||
}}
|
||||
class="pb-4"
|
||||
>
|
||||
<div class="mb-2">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={name}
|
||||
placeholder={t.namePlaceholder}
|
||||
required
|
||||
minlength={2}
|
||||
class="h-14 w-full rounded-xl border px-4 text-lg transition-colors focus:outline-none focus:ring-2"
|
||||
style="background-color: {isDark
|
||||
? 'rgba(0, 0, 0, 0.2)'
|
||||
: 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark
|
||||
? 'rgba(255, 255, 255, 0.2)'
|
||||
: 'rgba(0, 0, 0, 0.1)'}; color: {isDark
|
||||
? '#ffffff'
|
||||
: '#000000'}; --tw-ring-color: {primaryColor};"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<input
|
||||
type="email"
|
||||
|
|
@ -444,7 +487,8 @@
|
|||
<button
|
||||
type="button"
|
||||
onclick={() => (showPassword = !showPassword)}
|
||||
class="absolute inset-y-0 right-0 flex items-center justify-center w-14 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors"
|
||||
class="absolute top-1/2 -translate-y-1/2 right-4 flex items-center justify-center transition-colors"
|
||||
style="color: {isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)'};"
|
||||
aria-label={showPassword ? t.hidePassword : t.showPassword}
|
||||
>
|
||||
{#if showPassword}
|
||||
|
|
@ -476,7 +520,8 @@
|
|||
<button
|
||||
type="button"
|
||||
onclick={() => (showConfirmPassword = !showConfirmPassword)}
|
||||
class="absolute inset-y-0 right-0 flex items-center justify-center w-14 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors"
|
||||
class="absolute top-1/2 -translate-y-1/2 right-4 flex items-center justify-center transition-colors"
|
||||
style="color: {isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)'};"
|
||||
aria-label={showConfirmPassword ? t.hidePassword : t.showPassword}
|
||||
>
|
||||
{#if showConfirmPassword}
|
||||
|
|
|
|||
|
|
@ -103,14 +103,20 @@ export function createAuthService(config: AuthServiceConfig) {
|
|||
},
|
||||
|
||||
/**
|
||||
* Sign up with email and password
|
||||
* Sign up with email, password, and name
|
||||
* @param email User email
|
||||
* @param password User password
|
||||
* @param name User display name (min 2 characters)
|
||||
* @param referralCode Optional referral code for bonus credits
|
||||
*/
|
||||
async signUp(email: string, password: string, referralCode?: string): Promise<AuthResult> {
|
||||
async signUp(
|
||||
email: string,
|
||||
password: string,
|
||||
name: string,
|
||||
referralCode?: string
|
||||
): Promise<AuthResult> {
|
||||
try {
|
||||
const body: Record<string, string> = { email, password };
|
||||
const body: Record<string, string> = { email, password, name };
|
||||
if (referralCode) {
|
||||
body.referralCode = referralCode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
},
|
||||
"register": {
|
||||
"title": "Konto erstellen",
|
||||
"namePlaceholder": "Name",
|
||||
"nameRequired": "Name ist erforderlich",
|
||||
"nameTooShort": "Name muss mindestens 2 Zeichen lang sein",
|
||||
"emailPlaceholder": "E-Mail",
|
||||
"passwordPlaceholder": "Passwort",
|
||||
"confirmPasswordPlaceholder": "Passwort bestätigen",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
},
|
||||
"register": {
|
||||
"title": "Create Account",
|
||||
"namePlaceholder": "Name",
|
||||
"nameRequired": "Name is required",
|
||||
"nameTooShort": "Name must be at least 2 characters",
|
||||
"emailPlaceholder": "Email",
|
||||
"passwordPlaceholder": "Password",
|
||||
"confirmPasswordPlaceholder": "Confirm Password",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
},
|
||||
"register": {
|
||||
"title": "Crear Cuenta",
|
||||
"namePlaceholder": "Nombre",
|
||||
"nameRequired": "El nombre es obligatorio",
|
||||
"nameTooShort": "El nombre debe tener al menos 2 caracteres",
|
||||
"emailPlaceholder": "Correo electrónico",
|
||||
"passwordPlaceholder": "Contraseña",
|
||||
"confirmPasswordPlaceholder": "Confirmar Contraseña",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
},
|
||||
"register": {
|
||||
"title": "Créer un compte",
|
||||
"namePlaceholder": "Nom",
|
||||
"nameRequired": "Le nom est requis",
|
||||
"nameTooShort": "Le nom doit contenir au moins 2 caractères",
|
||||
"emailPlaceholder": "Email",
|
||||
"passwordPlaceholder": "Mot de passe",
|
||||
"confirmPasswordPlaceholder": "Confirmer le mot de passe",
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ export interface AuthTranslations {
|
|||
};
|
||||
register: {
|
||||
title: string;
|
||||
namePlaceholder: string;
|
||||
nameRequired: string;
|
||||
nameTooShort: string;
|
||||
emailPlaceholder: string;
|
||||
passwordPlaceholder: string;
|
||||
confirmPasswordPlaceholder: string;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
},
|
||||
"register": {
|
||||
"title": "Crea Account",
|
||||
"namePlaceholder": "Nome",
|
||||
"nameRequired": "Il nome è obbligatorio",
|
||||
"nameTooShort": "Il nome deve contenere almeno 2 caratteri",
|
||||
"emailPlaceholder": "Email",
|
||||
"passwordPlaceholder": "Password",
|
||||
"confirmPasswordPlaceholder": "Conferma Password",
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ export class AuthController {
|
|||
return this.betterAuthService.registerB2C({
|
||||
email: registerDto.email,
|
||||
password: registerDto.password,
|
||||
name: registerDto.name || '',
|
||||
name: registerDto.name,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { IsEmail, IsString, MinLength, MaxLength, IsOptional } from 'class-validator';
|
||||
import { IsEmail, IsString, MinLength, MaxLength } from 'class-validator';
|
||||
|
||||
export class RegisterDto {
|
||||
@IsEmail()
|
||||
|
|
@ -10,7 +10,7 @@ export class RegisterDto {
|
|||
password: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@MinLength(2, { message: 'Name must be at least 2 characters' })
|
||||
@MaxLength(255)
|
||||
name?: string;
|
||||
name: string;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue