mirror of
https://github.com/Balshgit/different
synced 2025-09-11 02:50:41 +03:00
add mail registration to app
This commit is contained in:
parent
196e9ab73b
commit
1b19a007ae
@ -51,8 +51,15 @@ EMAIL_HOST_PASSWORD=
|
|||||||
EMAIL_PORT=
|
EMAIL_PORT=
|
||||||
EMAIL_USE_SSL=
|
EMAIL_USE_SSL=
|
||||||
EMAIL_USE_TLS=
|
EMAIL_USE_TLS=
|
||||||
|
|
||||||
|
# ===== Google Recapthca =====
|
||||||
|
|
||||||
|
GOOGLE_RECAPTCHA_SECRET_KEY=
|
||||||
|
GOOGLE_RECAPTCHA_SECRET_SITE_KEY=
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## To urls.py
|
## To urls.py
|
||||||
|
|
||||||
from server.apps.accounts import urls as accounts_urls
|
from server.apps.accounts import urls as accounts_urls
|
||||||
@ -63,3 +70,7 @@ url_patterns
|
|||||||
path('admin/login/', login_required(lambda request: redirect('accounts/login/', permanent=True),
|
path('admin/login/', login_required(lambda request: redirect('accounts/login/', permanent=True),
|
||||||
redirect_field_name='admin/login/?next=')),
|
redirect_field_name='admin/login/?next=')),
|
||||||
|
|
||||||
|
|
||||||
|
## Add Google reCaptcha
|
||||||
|
|
||||||
|
https://evileg.com/uk/post/283/
|
@ -9,7 +9,7 @@ class CustomUserAdmin(UserAdmin):
|
|||||||
model = CustomUser
|
model = CustomUser
|
||||||
|
|
||||||
list_display = ('username', 'email', 'first_name', 'last_name', 'mobile', 'verification_code',
|
list_display = ('username', 'email', 'first_name', 'last_name', 'mobile', 'verification_code',
|
||||||
'is_superuser', 'is_staff', 'is_active', )
|
'is_superuser', 'is_staff', 'is_active', 'activation_token', )
|
||||||
|
|
||||||
list_filter = ('username', 'first_name', 'last_name', 'user_created', 'is_superuser', 'is_staff', 'is_active', )
|
list_filter = ('username', 'first_name', 'last_name', 'user_created', 'is_superuser', 'is_staff', 'is_active', )
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
# Generated by Django 3.2.8 on 2021-10-24 11:03
|
# Generated by Django 3.2.8 on 2021-10-27 15:03
|
||||||
|
|
||||||
import django.contrib.auth.models
|
|
||||||
import django.contrib.auth.validators
|
import django.contrib.auth.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
@ -32,6 +30,7 @@ class Migration(migrations.Migration):
|
|||||||
('mobile', models.CharField(blank=True, help_text='Users mobile phone', max_length=15, null=True, unique=True)),
|
('mobile', models.CharField(blank=True, help_text='Users mobile phone', max_length=15, null=True, unique=True)),
|
||||||
('verification_code', models.CharField(blank=True, help_text='Verification code for bot account', max_length=10, null=True, unique=True)),
|
('verification_code', models.CharField(blank=True, help_text='Verification code for bot account', max_length=10, null=True, unique=True)),
|
||||||
('user_created', models.DateField(auto_now_add=True, help_text='Date when user has been created', verbose_name='User created')),
|
('user_created', models.DateField(auto_now_add=True, help_text='Date when user has been created', verbose_name='User created')),
|
||||||
|
('activation_token', models.CharField(blank=True, help_text='Activation token for any user', max_length=20, null=True)),
|
||||||
('email', models.EmailField(help_text='User email', max_length=30, null=True, unique=True)),
|
('email', models.EmailField(help_text='User email', max_length=30, null=True, unique=True)),
|
||||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||||
|
@ -11,6 +11,8 @@ class CustomUser(AbstractUser):
|
|||||||
help_text='Verification code for bot account')
|
help_text='Verification code for bot account')
|
||||||
user_created = models.DateField(editable=False, auto_now_add=True, verbose_name='User created',
|
user_created = models.DateField(editable=False, auto_now_add=True, verbose_name='User created',
|
||||||
help_text='Date when user has been created')
|
help_text='Date when user has been created')
|
||||||
|
activation_token = models.CharField(max_length=20, null=True, blank=True,
|
||||||
|
help_text='Activation token for any user')
|
||||||
email = models.EmailField(max_length=30, unique=True, blank=False, null=True, help_text='User email')
|
email = models.EmailField(max_length=30, unique=True, blank=False, null=True, help_text='User email')
|
||||||
USERNAME_FIELD = 'username'
|
USERNAME_FIELD = 'username'
|
||||||
REQUIRED_FIELDS = ['email']
|
REQUIRED_FIELDS = ['email']
|
||||||
@ -18,3 +20,46 @@ class CustomUser(AbstractUser):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
|
|
||||||
|
# # ----- ToDO: Enable this to email verification --------
|
||||||
|
#
|
||||||
|
# from django.dispatch import receiver
|
||||||
|
# from django.conf import settings
|
||||||
|
# from .tasks import mail_send
|
||||||
|
# from server.settings.components.logging import main_logger
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# def user_tokens() -> dict:
|
||||||
|
#
|
||||||
|
# tokens_dict = dict()
|
||||||
|
#
|
||||||
|
# def generate_token(token_length: int) -> str:
|
||||||
|
# from random import choice
|
||||||
|
# token_chars = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890'
|
||||||
|
# generated_token = ''
|
||||||
|
# for i in range(token_length):
|
||||||
|
# generated_token += choice(token_chars)
|
||||||
|
# return generated_token
|
||||||
|
#
|
||||||
|
# tokens_dict['activation_token'] = generate_token(token_length=20)
|
||||||
|
#
|
||||||
|
# return tokens_dict
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# @receiver(models.signals.post_save, sender=CustomUser)
|
||||||
|
# def post_save_user_signal_handler(sender, instance, created, **kwargs):
|
||||||
|
#
|
||||||
|
# if created and instance.username != 'admin':
|
||||||
|
# instance.activation_token = user_tokens()['activation_token']
|
||||||
|
# instance.save()
|
||||||
|
# try:
|
||||||
|
# user = CustomUser.objects.get(username=instance.username)
|
||||||
|
# email = instance.email
|
||||||
|
# subject = 'Welcome to book bot administration'
|
||||||
|
# username = f'{instance.first_name} {instance.last_name}'
|
||||||
|
# text = f'https://{settings.DOMAIN_NAME}/accounts/complete_registration/{user.activation_token}'
|
||||||
|
#
|
||||||
|
# mail_send(to_email=email, subject=subject, username=username, text_content=text)
|
||||||
|
# except Exception as e:
|
||||||
|
# main_logger.error(f'Email not send to user {instance.username}. Reason: {e}')
|
||||||
|
@ -10,7 +10,6 @@ def mail_send(to_email: str, subject: str, text_content: str = '', **kwargs) ->
|
|||||||
username = kwargs.get('username')
|
username = kwargs.get('username')
|
||||||
from_email = settings.DEFAULT_FROM_EMAIL
|
from_email = settings.DEFAULT_FROM_EMAIL
|
||||||
|
|
||||||
msg_html = render_to_string('registration/message.html',
|
msg_html = render_to_string('registration/message.html', {'username': username, 'text_content': text_content})
|
||||||
{'username': username})
|
|
||||||
|
|
||||||
send_mail(subject, text_content, from_email, [to_email], html_message=msg_html, fail_silently=False)
|
send_mail(subject, '', from_email, [to_email], html_message=msg_html, fail_silently=False)
|
||||||
|
29
django_accounts_app/templates/registration/info.html
Normal file
29
django_accounts_app/templates/registration/info.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,700">
|
||||||
|
<title>Bootstrap Simple Login Form with Blue Background</title>
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
color: #fff;
|
||||||
|
background: #3598dc;
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
}
|
||||||
|
.info-form {
|
||||||
|
padding-top: 100px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="info-form" >
|
||||||
|
<h3>{{message}}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -98,7 +98,11 @@ body {
|
|||||||
<hr>
|
<hr>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="username" placeholder="Username" required="required">
|
{% if form.username.value %}
|
||||||
|
<input type="text" class="form-control" name="username" value="{{ form.username.value }}">
|
||||||
|
{% else %}
|
||||||
|
<input type="text" class="form-control" name="username" placeholder="Username" required="required">
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="password" class="form-control" name="password" placeholder="Password" required="required">
|
<input type="password" class="form-control" name="password" placeholder="Password" required="required">
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div>
|
||||||
<p style="color: green">Добро пожаловать, {{ username }}!</p>
|
<p style="color: green">Добро пожаловать, {{username}}!</p>
|
||||||
|
<p style="color: green">Чтобы активировать учётную запись перейдите по ссылке:
|
||||||
|
<a href="{{text_content}}">Завершить регистрацию</a></p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
@ -93,7 +93,6 @@ body {
|
|||||||
</style>
|
</style>
|
||||||
<div class="signup-form">
|
<div class="signup-form">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<h2>Sign Up</h2>
|
|
||||||
<p>Please fill in this form to create an account!</p>
|
<p>Please fill in this form to create an account!</p>
|
||||||
<hr>
|
<hr>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@ -131,9 +130,8 @@ body {
|
|||||||
{% if form.mobile.value %}
|
{% if form.mobile.value %}
|
||||||
<input type="text" class="form-control" name="mobile" value="{{ form.mobile.value }}">
|
<input type="text" class="form-control" name="mobile" value="{{ form.mobile.value }}">
|
||||||
{% else %}
|
{% else %}
|
||||||
<input id="mobile" type="row" class="form-control" name="mobile" placeholder="Mobile" >
|
<input id="mobile" type="row" class="form-control" name="mobile" placeholder="Mobile (Not required)">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<label for="mobile">Not required</label>
|
|
||||||
<div class="form-group">{{ form.mobile.errors }}</div>
|
<div class="form-group">{{ form.mobile.errors }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -144,9 +142,17 @@ body {
|
|||||||
<div class="form-group">{{ form.password2.errors }}</div>
|
<div class="form-group">{{ form.password2.errors }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src='https://www.google.com/recaptcha/api.js'></script>
|
||||||
|
<div class="form-group g-recaptcha" data-sitekey="6LcaYfgcAAAAAEX1GE9myoMEA2xVhWOrXqKUke_j"></div>
|
||||||
|
{% if messages %}
|
||||||
|
{% for message in messages %}
|
||||||
|
{{ message }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="submit" class="btn btn-primary btn-lg">Sign Up</button>
|
<button type="submit" class="btn btn-primary btn-lg">Sign Up</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">{{ form.non_field_errors }}</div>
|
<div class="form-group">{{ form.non_field_errors }}</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="hint-text">Already have an account? <a href="{% url 'accounts:login' %}">Login here</a></div>
|
<div class="hint-text">Already have an account? <a href="{% url 'accounts:login' %}">Login here</a></div>
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from .views import dashboard, RegisterUser
|
from .views import dashboard, RegisterUser, success_registration, not_registered
|
||||||
|
from server.apps.accounts.utils import check_recaptcha
|
||||||
|
|
||||||
app_name = 'accounts'
|
app_name = 'accounts'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', include("django.contrib.auth.urls")),
|
path('', include("django.contrib.auth.urls")),
|
||||||
path('dashboard/', dashboard, name='dashboard'),
|
path('dashboard/', dashboard, name='dashboard'),
|
||||||
path('registration/', RegisterUser.as_view(), name='registration')
|
path('registration/', check_recaptcha(RegisterUser.as_view()), name='registration'),
|
||||||
|
path('complete_registration/<str:activation_token>', success_registration, name='success_registration'),
|
||||||
]
|
]
|
||||||
|
27
django_accounts_app/utils.py
Normal file
27
django_accounts_app/utils.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
def check_recaptcha(function):
|
||||||
|
@wraps(function)
|
||||||
|
def new_func(request, *args, **kwargs):
|
||||||
|
request.recaptcha_is_valid = None
|
||||||
|
if request.method == 'POST':
|
||||||
|
recaptcha_response = request.POST.get('g-recaptcha-response')
|
||||||
|
data = {
|
||||||
|
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
|
||||||
|
'response': recaptcha_response
|
||||||
|
}
|
||||||
|
r = requests.post('https://www.google.com/recaptcha/api/siteverify', data=data)
|
||||||
|
result = r.json()
|
||||||
|
if result['success']:
|
||||||
|
request.recaptcha_is_valid = True
|
||||||
|
else:
|
||||||
|
request.recaptcha_is_valid = False
|
||||||
|
messages.error(request, 'Invalid reCAPTCHA. Please try again.')
|
||||||
|
return function(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return new_func
|
@ -2,33 +2,44 @@ from django.forms import BaseModelForm
|
|||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
from django.views.generic import CreateView
|
from django.views.generic import CreateView
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse, HttpRequest
|
||||||
from server.apps.accounts.forms import CustomUserCreationForm
|
from server.apps.accounts.forms import CustomUserCreationForm
|
||||||
|
from server.apps.accounts.models import CustomUser
|
||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
from .tasks import mail_send
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
def dashboard(request):
|
def dashboard(request: HttpRequest) -> HttpResponse:
|
||||||
return render(request, "users/dashboard.html", {})
|
return render(request, "users/dashboard.html", {})
|
||||||
|
|
||||||
|
|
||||||
|
def not_registered(request: HttpRequest) -> HttpResponse:
|
||||||
|
return redirect('accounts:login')
|
||||||
|
|
||||||
|
|
||||||
|
def success_registration(request: HttpRequest, activation_token: str) -> HttpResponse:
|
||||||
|
try:
|
||||||
|
user = CustomUser.objects.get(activation_token=activation_token)
|
||||||
|
user.backend = 'django.contrib.auth.backends.ModelBackend'
|
||||||
|
user.is_staff = True
|
||||||
|
user.activation_token = ''
|
||||||
|
user.save()
|
||||||
|
login(request, user)
|
||||||
|
return redirect('admin:index')
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
message = 'Activation token not found'
|
||||||
|
return render(request, 'registration/info.html', {'message': message}, status=404)
|
||||||
|
|
||||||
|
|
||||||
class RegisterUser(CreateView):
|
class RegisterUser(CreateView):
|
||||||
form_class = CustomUserCreationForm
|
form_class = CustomUserCreationForm
|
||||||
template_name = 'users/register.html'
|
template_name = 'users/register.html'
|
||||||
|
|
||||||
def form_valid(self, form: BaseModelForm) -> HttpResponse:
|
def form_valid(self, form: BaseModelForm) -> HttpResponse:
|
||||||
user = form.save()
|
if self.request.recaptcha_is_valid:
|
||||||
user.backend = 'django.contrib.auth.backends.ModelBackend'
|
form.save()
|
||||||
|
validate_email(form.instance.email)
|
||||||
validate_email(form.instance.email)
|
message = 'Please check your email for continue registration'
|
||||||
email = form.instance.email
|
return render(self.request, 'registration/info.html', {'message': message})
|
||||||
username = f'{form.instance.first_name} {form.instance.last_name}'
|
return render(self.request, 'users/register.html', self.get_context_data())
|
||||||
subject = 'Welcome to book bot administration'
|
|
||||||
mail_send(to_email=email, subject=subject, username=username)
|
|
||||||
|
|
||||||
login(self.request, user)
|
|
||||||
|
|
||||||
return redirect('admin:index')
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user