feat(auth): подтверждение email + актуализация SMTP/Ollama конфигов
- Резенд письма верификации (/auth/resend-verification), модалка на фронте с поллингом статуса, страница /verify-email/:token - SMTP переведён на собственный Postfix (mail.jze9mail.ru, STARTTLS, SMTP_TLS_VERIFY) вместо Yandex-заглушки в дефолтах и .env.example - OLLAMA_URL и модель в worker-gpu синхронизированы с новым GPU-хостом (llama3:8b -> qwen2.5:7b, которой раньше не было на сервере) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
83
services/frontend/src/pages/VerifyEmail.tsx
Normal file
83
services/frontend/src/pages/VerifyEmail.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useParams, useNavigate, Link } from 'react-router-dom';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { CheckCircle2, XCircle, Loader2 } from 'lucide-react';
|
||||
import { authApi } from '../api/client';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
|
||||
export function VerifyEmail() {
|
||||
const { token } = useParams<{ token: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { updateUser } = useAuthStore();
|
||||
|
||||
const verify = useMutation({
|
||||
mutationFn: () => authApi.verifyEmail(token!),
|
||||
onSuccess: () => {
|
||||
updateUser({ is_verified: true });
|
||||
setTimeout(() => navigate('/cabinet'), 3000);
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (token) verify.mutate();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [token]);
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto pt-16 text-center">
|
||||
<div className="bg-white rounded-2xl border border-gray-100 p-10">
|
||||
{verify.isPending && (
|
||||
<>
|
||||
<div className="flex justify-center mb-5">
|
||||
<Loader2 className="w-14 h-14 text-brand-600 animate-spin" />
|
||||
</div>
|
||||
<h1 className="text-xl font-bold text-gray-900 mb-2">Подтверждаем email…</h1>
|
||||
<p className="text-sm text-gray-500">Пожалуйста, подождите</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{verify.isSuccess && (
|
||||
<>
|
||||
<div className="flex justify-center mb-5">
|
||||
<div className="p-4 bg-green-50 rounded-full">
|
||||
<CheckCircle2 className="w-14 h-14 text-green-500" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">Email подтверждён!</h1>
|
||||
<div className="inline-flex items-center gap-2 px-4 py-2 bg-green-50 rounded-xl mb-4">
|
||||
<CheckCircle2 className="w-4 h-4 text-green-500" />
|
||||
<span className="text-sm font-medium text-green-700">Email подтверждён</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500">
|
||||
Аккаунт активирован. Перенаправляем в личный кабинет…
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
{verify.isError && (
|
||||
<>
|
||||
<div className="flex justify-center mb-5">
|
||||
<div className="p-4 bg-red-50 rounded-full">
|
||||
<XCircle className="w-14 h-14 text-red-500" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">Ошибка подтверждения</h1>
|
||||
<div className="inline-flex items-center gap-2 px-4 py-2 bg-red-50 rounded-xl mb-4">
|
||||
<XCircle className="w-4 h-4 text-red-500" />
|
||||
<span className="text-sm font-medium text-red-700">Email не подтверждён</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mb-6">
|
||||
Ссылка недействительна или устарела. Запросите новое письмо в личном кабинете.
|
||||
</p>
|
||||
<Link
|
||||
to="/cabinet"
|
||||
className="inline-block px-6 py-2.5 bg-brand-600 text-white rounded-xl text-sm font-medium hover:bg-brand-700 transition-colors"
|
||||
>
|
||||
В личный кабинет
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user