diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index 50a950b..d9c234d 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -117,6 +117,10 @@ const auth: FastifyPluginAsync = async (fastify) => { async (request, reply) => { const { hotelName, address, contact, email, phone, password } = request.body + if (!/[a-zA-Zа-яА-ЯёЁ]/.test(password)) { + return reply.code(400).send({ error: 'Пароль должен содержать хотя бы одну букву' }) + } + const { rows: existing } = await db.query( 'SELECT id FROM users WHERE email = $1', [email.toLowerCase().trim()], @@ -346,6 +350,11 @@ const auth: FastifyPluginAsync = async (fastify) => { }, async (request, reply) => { const { token, password } = request.body + + if (!/[a-zA-Zа-яА-ЯёЁ]/.test(password)) { + return reply.code(400).send({ error: 'Пароль должен содержать хотя бы одну букву' }) + } + const { rows } = await db.query( `SELECT id FROM users WHERE reset_token = $1 diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index fb0e772..d5cb015 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -330,6 +330,7 @@ function RegisterForm({ onSwitch }: { onSwitch: () => void }) { if (!email.trim()) err.email = 'Введите email' else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) err.email = 'Некорректный email' if (password.length < 8) err.password = 'Минимум 8 символов' + else if (!/[a-zA-Zа-яА-ЯёЁ]/.test(password)) err.password = 'Пароль должен содержать хотя бы одну букву' setErrors(err) if (Object.keys(err).length > 0) return setLoading(true) @@ -380,8 +381,9 @@ function RegisterForm({ onSwitch }: { onSwitch: () => void }) { type="text" className={cn('input', errors.hotelName && 'border-red-400 focus:ring-red-400')} placeholder="Grand Palace Hotel" + autoCapitalize="words" value={hotelName} - onChange={e => { setHotelName(e.target.value); setErrors(prev => ({ ...prev, hotelName: '' })) }} + onChange={e => { setHotelName(e.target.value.replace(/(?:^|\s)\S/g, c => c.toUpperCase())); setErrors(prev => ({ ...prev, hotelName: '' })) }} /> @@ -399,8 +401,9 @@ function RegisterForm({ onSwitch }: { onSwitch: () => void }) { type="text" className={cn('input', errors.contact && 'border-red-400 focus:ring-red-400')} placeholder="Иванов Иван Иванович" + autoCapitalize="words" value={contact} - onChange={e => { setContact(e.target.value); setErrors(prev => ({ ...prev, contact: '' })) }} + onChange={e => { setContact(e.target.value.replace(/(?:^|\s)\S/g, c => c.toUpperCase())); setErrors(prev => ({ ...prev, contact: '' })) }} /> @@ -438,9 +441,17 @@ function RegisterForm({ onSwitch }: { onSwitch: () => void }) { {password && ( -
- {[password.length >= 8, /[A-Z]/.test(password), /[0-9]/.test(password)].map((ok, i) => ( -
+
+ {[ + { ok: password.length >= 8, label: '≥ 8 символов' }, + { ok: /[a-zA-Zа-яА-ЯёЁ]/.test(password), label: 'Содержит букву' }, + { ok: /[A-ZА-ЯЁ]/.test(password), label: 'Заглавная буква' }, + { ok: /[0-9]/.test(password), label: 'Цифра' }, + ].map(({ ok, label }) => ( +
+
+ {label} +
))}
)} diff --git a/src/pages/ResetPasswordPage.tsx b/src/pages/ResetPasswordPage.tsx index c8e681e..c682b02 100644 --- a/src/pages/ResetPasswordPage.tsx +++ b/src/pages/ResetPasswordPage.tsx @@ -21,6 +21,7 @@ export function ResetPasswordPage() { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (password.length < 8) { setError('Минимум 8 символов'); return } + if (!/[a-zA-Zа-яА-ЯёЁ]/.test(password)) { setError('Пароль должен содержать хотя бы одну букву'); return } if (password !== password2) { setError('Пароли не совпадают'); return } setError('') setLoading(true) @@ -100,9 +101,17 @@ export function ResetPasswordPage() {
{password && ( -
- {[password.length >= 8, /[A-Z]/.test(password), /[0-9]/.test(password)].map((ok, i) => ( -
+
+ {[ + { ok: password.length >= 8, label: '≥ 8 символов' }, + { ok: /[a-zA-Zа-яА-ЯёЁ]/.test(password), label: 'Содержит букву' }, + { ok: /[A-ZА-ЯЁ]/.test(password), label: 'Заглавная буква' }, + { ok: /[0-9]/.test(password), label: 'Цифра' }, + ].map(({ ok, label }) => ( +
+
+ {label} +
))}
)}