mirror of
https://github.com/EDeev/deev.space.git
synced 2026-06-16 21:21:10 +03:00
251 lines
No EOL
8.3 KiB
Python
251 lines
No EOL
8.3 KiB
Python
from django import forms
|
||
from django.contrib.auth.forms import AuthenticationForm
|
||
from django.core.validators import RegexValidator
|
||
from django.conf import settings
|
||
import bleach
|
||
import requests
|
||
|
||
from .models import CustomUser, Comment, ContactMessage
|
||
|
||
|
||
class SmartCaptchaField(forms.CharField):
|
||
"""Кастомное поле для Yandex SmartCaptcha."""
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
kwargs.setdefault('required', True)
|
||
kwargs.setdefault('widget', forms.HiddenInput(attrs={'id': 'smart-captcha-token'}))
|
||
super().__init__(*args, **kwargs)
|
||
|
||
def validate(self, value):
|
||
super().validate(value)
|
||
if not value:
|
||
raise forms.ValidationError('Пожалуйста, пройдите проверку капчи')
|
||
|
||
# Валидация токена на сервере Yandex
|
||
if not self._verify_captcha(value):
|
||
raise forms.ValidationError('Проверка капчи не пройдена. Попробуйте снова.')
|
||
|
||
def _verify_captcha(self, token):
|
||
"""Проверка токена на сервере Yandex SmartCaptcha."""
|
||
try:
|
||
response = requests.post(
|
||
'https://smartcaptcha.yandexcloud.net/validate',
|
||
data={
|
||
'secret': settings.SMARTCAPTCHA_SERVER_KEY,
|
||
'token': token,
|
||
},
|
||
timeout=5
|
||
)
|
||
|
||
# Рекомендация документации: при ошибке HTTP считать проверку успешной
|
||
if response.status_code != 200:
|
||
return True
|
||
|
||
result = response.json()
|
||
return result.get('status') == 'ok'
|
||
|
||
except requests.RequestException:
|
||
# При сетевой ошибке пропускаем, чтобы не блокировать пользователей
|
||
return True
|
||
|
||
|
||
class SmartCaptchaWidget(forms.Widget):
|
||
"""Виджет для отображения Yandex SmartCaptcha."""
|
||
|
||
template_name = 'widgets/smartcaptcha.html'
|
||
|
||
def __init__(self, attrs=None):
|
||
default_attrs = {'class': 'smart-captcha-container'}
|
||
if attrs:
|
||
default_attrs.update(attrs)
|
||
super().__init__(attrs=default_attrs)
|
||
|
||
def get_context(self, name, value, attrs):
|
||
context = super().get_context(name, value, attrs)
|
||
context['client_key'] = settings.SMARTCAPTCHA_CLIENT_KEY
|
||
return context
|
||
|
||
|
||
class RegisterForm(forms.ModelForm):
|
||
"""Форма регистрации пользователя."""
|
||
username = forms.CharField(
|
||
max_length=30,
|
||
validators=[
|
||
RegexValidator(
|
||
regex=r'^[a-zA-Z0-9_]+$',
|
||
message='Имя пользователя может содержать только латинские буквы, цифры и подчёркивания'
|
||
)
|
||
],
|
||
widget=forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Имя пользователя (латиница)',
|
||
'autocomplete': 'username'
|
||
}),
|
||
label='Имя пользователя'
|
||
)
|
||
|
||
email = forms.EmailField(
|
||
required=False,
|
||
widget=forms.EmailInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Email (необязательно)',
|
||
'autocomplete': 'email'
|
||
}),
|
||
label='Email'
|
||
)
|
||
|
||
password = forms.CharField(
|
||
min_length=4,
|
||
widget=forms.PasswordInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Пароль (минимум 4 символа)',
|
||
'autocomplete': 'new-password'
|
||
}),
|
||
label='Пароль'
|
||
)
|
||
|
||
password_confirm = forms.CharField(
|
||
widget=forms.PasswordInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Подтвердите пароль',
|
||
'autocomplete': 'new-password'
|
||
}),
|
||
label='Подтверждение пароля'
|
||
)
|
||
|
||
captcha = SmartCaptchaField(label='')
|
||
|
||
class Meta:
|
||
model = CustomUser
|
||
fields = ['username', 'email']
|
||
|
||
def clean_username(self):
|
||
username = self.cleaned_data.get('username')
|
||
if CustomUser.objects.filter(username__iexact=username).exists():
|
||
raise forms.ValidationError('Пользователь с таким именем уже существует')
|
||
return username
|
||
|
||
def clean(self):
|
||
cleaned_data = super().clean()
|
||
password = cleaned_data.get('password')
|
||
password_confirm = cleaned_data.get('password_confirm')
|
||
|
||
if password and password_confirm and password != password_confirm:
|
||
self.add_error('password_confirm', 'Пароли не совпадают')
|
||
|
||
return cleaned_data
|
||
|
||
def save(self, commit=True):
|
||
user = super().save(commit=False)
|
||
user.set_password(self.cleaned_data['password'])
|
||
user.is_verified = True
|
||
if commit:
|
||
user.save()
|
||
return user
|
||
|
||
|
||
class LoginForm(AuthenticationForm):
|
||
"""Форма авторизации пользователя."""
|
||
username = forms.CharField(
|
||
widget=forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Имя пользователя',
|
||
'autocomplete': 'username'
|
||
}),
|
||
label='Имя пользователя'
|
||
)
|
||
|
||
password = forms.CharField(
|
||
widget=forms.PasswordInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Пароль',
|
||
'autocomplete': 'current-password'
|
||
}),
|
||
label='Пароль'
|
||
)
|
||
|
||
remember_me = forms.BooleanField(
|
||
required=False,
|
||
initial=True,
|
||
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||
label='Запомнить меня'
|
||
)
|
||
|
||
error_messages = {
|
||
'invalid_login': 'Неверное имя пользователя или пароль',
|
||
'inactive': 'Этот аккаунт деактивирован',
|
||
}
|
||
|
||
|
||
class CommentForm(forms.ModelForm):
|
||
"""Форма добавления комментария."""
|
||
content = forms.CharField(
|
||
max_length=2000,
|
||
widget=forms.Textarea(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Напишите комментарий...',
|
||
'rows': 3
|
||
}),
|
||
label=''
|
||
)
|
||
|
||
class Meta:
|
||
model = Comment
|
||
fields = ['content']
|
||
|
||
def clean_content(self):
|
||
content = self.cleaned_data.get('content')
|
||
allowed_tags = ['b', 'i', 'u', 'em', 'strong', 'a', 'code', 'pre']
|
||
allowed_attrs = {'a': ['href', 'title', 'rel']}
|
||
content = bleach.clean(content, tags=allowed_tags, attributes=allowed_attrs, strip=True)
|
||
content = bleach.linkify(content)
|
||
return content
|
||
|
||
|
||
class ContactForm(forms.ModelForm):
|
||
"""Форма обратной связи."""
|
||
name = forms.CharField(
|
||
max_length=100,
|
||
widget=forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Ваше имя'
|
||
}),
|
||
label='Имя'
|
||
)
|
||
|
||
email = forms.EmailField(
|
||
widget=forms.EmailInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Email для ответа'
|
||
}),
|
||
label='Email'
|
||
)
|
||
|
||
subject = forms.CharField(
|
||
max_length=200,
|
||
widget=forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Тема сообщения'
|
||
}),
|
||
label='Тема'
|
||
)
|
||
|
||
message = forms.CharField(
|
||
widget=forms.Textarea(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Текст сообщения',
|
||
'rows': 5
|
||
}),
|
||
label='Сообщение'
|
||
)
|
||
|
||
captcha = SmartCaptchaField(label='')
|
||
|
||
class Meta:
|
||
model = ContactMessage
|
||
fields = ['name', 'email', 'subject', 'message']
|
||
|
||
def clean_message(self):
|
||
message = self.cleaned_data.get('message')
|
||
message = bleach.clean(message, tags=[], strip=True)
|
||
return message |