- Added get_user_model import in accounts/views.py - Fixed User variable scope in password_setup_confirm view - Added accounts URLs to urls_public.py for password setup on main domain - Password setup link now accessible from public schema Technical details: - get_user_model() needed to be imported from django.contrib.auth - User model reference moved outside try block to fix UnboundLocalError - accounts.urls included in public URLconf for /accounts/setup-password/ route 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
288 lines
13 KiB
Python
288 lines
13 KiB
Python
from django.shortcuts import render, redirect, get_object_or_404
|
||
from django.contrib.auth import login, authenticate, logout, get_user_model
|
||
from django.contrib import messages
|
||
from django.core.mail import send_mail
|
||
from django.conf import settings
|
||
from django.urls import reverse
|
||
from django.shortcuts import redirect
|
||
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
|
||
from django.utils.encoding import force_bytes, force_str
|
||
from django.contrib.auth.tokens import default_token_generator
|
||
from django.contrib.auth.decorators import login_required
|
||
from django.contrib.auth import update_session_auth_hash
|
||
from django.contrib.auth.forms import PasswordChangeForm
|
||
from .forms import CustomUserCreationForm, PasswordResetForm
|
||
from .models import CustomUser
|
||
import uuid
|
||
|
||
|
||
def register(request):
|
||
if request.method == 'POST':
|
||
form = CustomUserCreationForm(request.POST)
|
||
if form.is_valid():
|
||
user = form.save(commit=False)
|
||
user.is_active = False # Пользователь не активен до подтверждения email
|
||
user.save()
|
||
|
||
# Отправляем письмо с подтверждением
|
||
confirmation_url = request.build_absolute_uri(
|
||
reverse('accounts:confirm_email', kwargs={'token': user.email_confirmation_token})
|
||
)
|
||
|
||
subject = 'Подтверждение Email'
|
||
message = f'Привет {user.name}!\n\nДля подтверждения вашего email перейдите по следующей ссылке: {confirmation_url}\n\nСпасибо за регистрацию!'
|
||
from_email = settings.DEFAULT_FROM_EMAIL
|
||
recipient_list = [user.email]
|
||
|
||
# Выводим письмо в консоль, как вы просили
|
||
print(f"Письмо для подтверждения:\nТема: {subject}\nСообщение:\n{message}\nПолучатель: {recipient_list}")
|
||
|
||
# В реальной системе отправили бы письмо:
|
||
# send_mail(subject, message, from_email, recipient_list, fail_silently=False)
|
||
|
||
messages.success(request, 'Пожалуйста, проверьте вашу почту для подтверждения email.')
|
||
return redirect('accounts:login')
|
||
else:
|
||
form = CustomUserCreationForm()
|
||
|
||
return render(request, 'register.html', {'form': form})
|
||
|
||
|
||
def register_view(request):
|
||
if request.method == 'POST':
|
||
form = CustomUserCreationForm(request.POST)
|
||
if form.is_valid():
|
||
user = form.save(commit=False)
|
||
user.is_active = False # Пользователь не активен до подтверждения email
|
||
user.save()
|
||
|
||
# Отправляем письмо с подтверждением (выводим в консоль)
|
||
confirmation_url = request.build_absolute_uri(
|
||
f'/accounts/confirm/{user.email_confirmation_token}/'
|
||
)
|
||
|
||
subject = 'Подтверждение Email'
|
||
message = f'Привет {user.name}!\n\nДля подтверждения вашего email перейдите по следующей ссылке: {confirmation_url}\n\nСпасибо за регистрацию!'
|
||
from_email = 'noreply@example.com' # Используем значение из настроек
|
||
recipient_list = [user.email]
|
||
|
||
# Выводим письмо в консоль, как вы просили
|
||
print(f"Письмо для подтверждения:\nТема: {subject}\nСообщение:\n{message}\nПолучатель: {recipient_list}")
|
||
|
||
messages.success(request, 'Пожалуйста, проверьте вашу почту для подтверждения email.')
|
||
return redirect('accounts:login') # Перенаправляем на страницу входа после регистрации
|
||
else:
|
||
form = CustomUserCreationForm()
|
||
|
||
return render(request, 'register.html', {'form': form})
|
||
|
||
|
||
def login_view(request):
|
||
if request.method == 'POST':
|
||
email = request.POST.get('email')
|
||
password = request.POST.get('password')
|
||
|
||
# Используем email как логин
|
||
user = authenticate(request, username=email, password=password)
|
||
|
||
if user is not None:
|
||
if user.is_email_confirmed: # Проверяем, подтвержден ли email
|
||
login(request, user)
|
||
# Перенаправляем на главную страницу после успешного входа
|
||
next_page = request.GET.get('next', 'index') # Если есть параметр next, переходим туда
|
||
return redirect(next_page)
|
||
else:
|
||
messages.error(request, 'Пожалуйста, подтвердите ваш email для входа.')
|
||
else:
|
||
messages.error(request, 'Неверный email или пароль.')
|
||
|
||
return render(request, 'login.html')
|
||
|
||
|
||
def logout_view(request):
|
||
logout(request)
|
||
return redirect('index')
|
||
|
||
|
||
@login_required
|
||
def profile_view(request):
|
||
return render(request, 'profile.html', {'user': request.user})
|
||
|
||
|
||
@login_required
|
||
def change_password_view(request):
|
||
if request.method == 'POST':
|
||
form = PasswordChangeForm(request.user, request.POST)
|
||
if form.is_valid():
|
||
user = form.save()
|
||
update_session_auth_hash(request, user) # Important for keeping the user logged in
|
||
messages.success(request, 'Ваш пароль был успешно изменен!')
|
||
return redirect('profile')
|
||
else:
|
||
messages.error(request, 'Пожалуйста, исправьте ошибки в форме.')
|
||
else:
|
||
form = PasswordChangeForm(request.user)
|
||
|
||
return render(request, 'change_password.html', {'form': form})
|
||
|
||
|
||
def confirm_email(request, token):
|
||
user = get_object_or_404(CustomUser, email_confirmation_token=token)
|
||
|
||
if user.is_email_confirmed:
|
||
messages.info(request, 'Email уже был подтвержден.')
|
||
else:
|
||
user.confirm_email()
|
||
user.is_active = True # Активируем пользователя
|
||
user.save()
|
||
messages.success(request, 'Email успешно подтвержден! Теперь вы можете войти.')
|
||
|
||
return redirect('accounts:login')
|
||
|
||
|
||
def password_reset_request(request):
|
||
if request.method == 'POST':
|
||
form = PasswordResetForm(request.POST)
|
||
if form.is_valid():
|
||
email = form.cleaned_data['email']
|
||
try:
|
||
user = CustomUser.objects.get(email=email)
|
||
|
||
# Генерируем токен восстановления
|
||
user.password_reset_token = uuid.uuid4()
|
||
user.save()
|
||
|
||
# Отправляем письмо с инструкциями по восстановлению
|
||
reset_url = request.build_absolute_uri(
|
||
reverse('accounts:password_reset_confirm', kwargs={'token': user.password_reset_token})
|
||
)
|
||
|
||
subject = 'Восстановление пароля'
|
||
message = f'Привет {user.name}!\n\nДля восстановления пароля перейдите по следующей ссылке: {reset_url}\n\nЕсли вы не запрашивали восстановление пароля, проигнорируйте это письмо.'
|
||
from_email = settings.DEFAULT_FROM_EMAIL
|
||
recipient_list = [user.email]
|
||
|
||
# Выводим письмо в консоль
|
||
print(f"Письмо для восстановления пароля:\nТема: {subject}\nСообщение:\n{message}\nПолучатель: {recipient_list}")
|
||
|
||
messages.success(request, f'Инструкции по восстановлению пароля отправлены на {email}')
|
||
except CustomUser.DoesNotExist:
|
||
# Для безопасности не сообщаем, что пользователя не существует
|
||
messages.success(request, 'Если аккаунт с таким email существует, инструкции по восстановлению пароля были отправлены на него.')
|
||
|
||
return redirect('accounts:login')
|
||
else:
|
||
form = PasswordResetForm()
|
||
|
||
return render(request, 'login.html', {'form': form})
|
||
|
||
|
||
def password_reset_confirm(request, token):
|
||
try:
|
||
user = CustomUser.objects.get(password_reset_token=token)
|
||
except CustomUser.DoesNotExist:
|
||
messages.error(request, 'Ссылка для восстановления пароля недействительна.')
|
||
return redirect('index')
|
||
|
||
if request.method == 'POST':
|
||
password1 = request.POST.get('password1')
|
||
password2 = request.POST.get('password2')
|
||
|
||
if password1 and password2 and password1 == password2:
|
||
user.set_password(password1)
|
||
user.password_reset_token = None # Обнуляем токен
|
||
user.save()
|
||
messages.success(request, 'Пароль успешно изменен. Теперь вы можете войти.')
|
||
return redirect('accounts:login')
|
||
else:
|
||
messages.error(request, 'Пароли не совпадают.')
|
||
|
||
# Отображаем форму смены пароля
|
||
return render(request, 'accounts/password_reset_confirm.html', {'user': user})
|
||
|
||
|
||
def password_setup_confirm(request, token):
|
||
"""
|
||
Позволить владельцу тенанта установить начальный пароль после одобрения регистрации.
|
||
Похоже на сброс пароля, но для новых аккаунтов.
|
||
"""
|
||
from tenants.models import TenantRegistration
|
||
from datetime import timedelta
|
||
from django.utils import timezone
|
||
|
||
# Найти регистрацию по токену
|
||
try:
|
||
registration = TenantRegistration.objects.get(
|
||
password_setup_token=token,
|
||
status=TenantRegistration.STATUS_APPROVED
|
||
)
|
||
except TenantRegistration.DoesNotExist:
|
||
messages.error(request, 'Ссылка для настройки пароля недействительна.')
|
||
return redirect('index')
|
||
|
||
# Проверить истечение токена (7 дней)
|
||
if registration.password_setup_token_created_at:
|
||
expires_at = registration.password_setup_token_created_at + timedelta(days=7)
|
||
if timezone.now() > expires_at:
|
||
messages.error(
|
||
request,
|
||
'Ссылка для настройки пароля истекла. Пожалуйста, свяжитесь с поддержкой.'
|
||
)
|
||
return redirect('index')
|
||
|
||
# Получить тенант и пользователя-владельца
|
||
from django.db import connection
|
||
tenant = registration.tenant
|
||
if not tenant:
|
||
messages.error(request, 'Тенант не найден.')
|
||
return redirect('index')
|
||
|
||
# Переключиться на схему тенанта чтобы найти владельца
|
||
connection.set_tenant(tenant)
|
||
User = get_user_model()
|
||
try:
|
||
owner = User.objects.get(email=registration.owner_email)
|
||
except User.DoesNotExist:
|
||
connection.set_schema_to_public()
|
||
messages.error(request, 'Пользователь не найден.')
|
||
return redirect('index')
|
||
|
||
# Обработать POST - установить пароль
|
||
if request.method == 'POST':
|
||
password1 = request.POST.get('password1')
|
||
password2 = request.POST.get('password2')
|
||
|
||
if password1 and password2 and password1 == password2:
|
||
# Установить пароль и активировать аккаунт
|
||
owner.set_password(password1)
|
||
owner.is_active = True
|
||
owner.save()
|
||
|
||
# Очистить токен
|
||
connection.set_schema_to_public()
|
||
registration.password_setup_token = None
|
||
registration.password_setup_token_created_at = None
|
||
registration.save()
|
||
|
||
# Автоматический вход
|
||
connection.set_tenant(tenant)
|
||
login(request, owner, backend='django.contrib.auth.backends.ModelBackend')
|
||
|
||
messages.success(
|
||
request,
|
||
f'Пароль успешно установлен! Добро пожаловать в {tenant.name}!'
|
||
)
|
||
|
||
# Перенаправить на домен тенанта
|
||
tenant_url = f'http://{tenant.schema_name}.localhost:8000/'
|
||
return redirect(tenant_url)
|
||
else:
|
||
messages.error(request, 'Пароли не совпадают.')
|
||
|
||
connection.set_schema_to_public()
|
||
|
||
# Отрисовать форму установки пароля
|
||
return render(request, 'accounts/password_setup_confirm.html', {
|
||
'registration': registration,
|
||
'tenant': tenant
|
||
}) |