Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add SMTP backend, email settings and templates #71

Merged
merged 3 commits into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/blackfox_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ jobs:
sudo docker volume rm -f root_frontend_build
echo ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} > .env
echo DJANGO_SECRET_KEY=${{ secrets.DJANGO_SECRET_KEY }} >> .env
echo EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }} >> .env
echo EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }} >> .env
echo DB_ENGINE=${{ secrets.DB_ENGINE }} >> .env
echo DB_HOST=${{ secrets.DB_HOST }} >> .env
echo DB_PORT=${{ secrets.DB_PORT }} >> .env
Expand Down
8 changes: 6 additions & 2 deletions blackfox/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

fatsecret_account_not_exists_message = 'Please link your Fatsecret account'
fatsecret_error_message = 'Fatsecret error: {error}'
fooddiary_objects_create_message = 'Fooddiary objects successfully created'
project_not_exists_message = 'Please create a project for current user'


Expand All @@ -40,7 +41,7 @@ def get_queryset(self):


class FoodDiaryViewSet(viewsets.ModelViewSet):
"""A viewset for viewing and editing FoodDiary instances."""
"""A viewset for creating and viewing FoodDiary instances."""

permission_classes = [IsAuthenticated]
serializer_class = FoodDiarySerializer
Expand Down Expand Up @@ -77,7 +78,10 @@ def create(self, request):
status=status.HTTP_400_BAD_REQUEST
)
FoodDiary.objects.bulk_create(objs=objs)
return Response(FoodDiarySerializer(objs, many=True).data)
return Response(
{'message': fooddiary_objects_create_message},
status=status.HTTP_201_CREATED
)


class ProjectViewSet(viewsets.ModelViewSet):
Expand Down
38 changes: 36 additions & 2 deletions blackfox/blackfox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
Expand Down Expand Up @@ -106,6 +106,13 @@
}
}

'''DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': '/Users/evgeniygolodnykh/Desktop/blackfox_backend/db.sqlite3',
}
}'''


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
Expand Down Expand Up @@ -158,6 +165,24 @@
CORS_ALLOW_ALL_ORIGINS = True


# SMTP backend settings
# https://docs.djangoproject.com/en/5.1/topics/email/

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

EMAIL_HOST = 'smtp.gmail.com'

EMAIL_PORT = 587

EMAIL_USE_TLS = True

EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER')

EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD')

DEFAULT_FROM_EMAIL = EMAIL_HOST_USER


# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

Expand All @@ -182,6 +207,15 @@
'LOGIN_FIELD': 'email',
'HIDE_USERS': False,
'SET_PASSWORD_RETYPE': True,
'PASSWORD_RESET_CONFIRM_RETYPE': True,
'SEND_ACTIVATION_EMAIL': True,
'PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND': True,
'ACTIVATION_URL': 'auth/activate/?uid={uid}&token={token}',
'PASSWORD_RESET_CONFIRM_URL': 'auth/set_password/?uid={uid}&token={token}',
'EMAIL': {
'activation': 'users.email.ActivationEmail',
'password_reset': 'users.email.PasswordResetEmail',
},
'SERIALIZERS': {
'user': 'users.serializers.CustomUserSerializer',
'user_create': 'users.serializers.CustomUserCreateSerializer',
Expand All @@ -191,7 +225,7 @@
'PERMISSIONS': {
'user': ['djoser.permissions.CurrentUserOrAdminOrReadOnly'],
'user_delete': ['api.permissions.IsAdmin'],
'user_list': ['djoser.permissions.CurrentUserOrAdminOrReadOnly'],
'user_list': ['api.permissions.IsAdminOrCoach'],
}
}

Expand Down
27 changes: 27 additions & 0 deletions blackfox/templates/email/activation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% load i18n %}

{% block subject %}
{% blocktrans %}Account activation on BlackFox Nutrition website{% endblocktrans %}
{% endblock subject %}

{% block text_body %}
{% blocktrans %}You're receiving this email because you need to finish activation process on BlackFox Nutrition website.{% endblocktrans %}

{% trans "Please go to the following page to activate account:" %}
https://www.blackfoxnutrition.ru/{{ url|safe }}

{% trans "Thanks for using our site!" %}

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endblock text_body %}

{% block html_body %}
<p>{% blocktrans %}You're receiving this email because you need to finish activation process on BlackFox Nutrition website.{% endblocktrans %}</p>

<p>{% trans "Please go to the following page to activate account:" %}</p>
<p><a href="https://www.blackfoxnutrition.ru/{{ url|safe }}">https://www.blackfoxnutrition.ru/{{ url|safe }}</a></p>

<p>{% trans "Thanks for using our site!" %}</p>

<p>{% blocktrans %}The BlackFox Nutrition team{% endblocktrans %}</p>
{% endblock html_body %}
29 changes: 29 additions & 0 deletions blackfox/templates/email/password_reset.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% load i18n %}

{% block subject %}
{% blocktrans %}Password reset on BlackFox Nutrition website{% endblocktrans %}
{% endblock subject %}

{% block text_body %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at BlackFox Nutrition website.{% endblocktrans %}

{% trans "Please go to the following page and choose a new password:" %}
https://www.blackfoxnutrition.ru/{{ url|safe }}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}

{% trans "Thanks for using our site!" %}

{% blocktrans %}The BlackFox Nutrition team{% endblocktrans %}
{% endblock text_body %}

{% block html_body %}
<p>{% blocktrans %}You're receiving this email because you requested a password reset for your user account at BlackFox Nutrition website.{% endblocktrans %}</p>

<p>{% trans "Please go to the following page and choose a new password:" %}</p>
<a href="https://www.blackfoxnutrition.ru/{{ url|safe }}">https://www.blackfoxnutrition.ru/{{ url|safe }}</a>
<p>{% trans "Your username, in case you've forgotten:" %} <b>{{ user.get_username }}</b></p>

<p>{% trans "Thanks for using our site!" %}</p>

<p>{% blocktrans %}The BlackFox Nutrition team{% endblocktrans %}</p>
{% endblock html_body %}
13 changes: 13 additions & 0 deletions blackfox/users/email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from djoser import email


class ActivationEmail(email.ActivationEmail):
'''Override activation email with template'''

template_name = 'email/activation.html'


class PasswordResetEmail(email.PasswordResetEmail):
'''Override reset password email with template'''

template_name = 'email/password_reset.html'
11 changes: 11 additions & 0 deletions blackfox/users/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from django.shortcuts import get_object_or_404
from djoser.compat import get_user_email_field_name
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

Expand Down Expand Up @@ -137,6 +138,7 @@ def create(self, validated_data):
role=validated_data['role'].lower(),
first_name=validated_data['first_name'].capitalize(),
last_name=validated_data['last_name'].capitalize(),
is_active=False,
)
user.set_password(validated_data['password'])
user.save()
Expand Down Expand Up @@ -186,6 +188,15 @@ def validate_image(self, value):
raise serializers.ValidationError(error_image_message)
return value

def update(self, instance, validated_data):
email_field = get_user_email_field_name(User)
instance.email_changed = False
if email_field in validated_data:
instance.is_active = False
instance.email_changed = True
instance.save(update_fields=['is_active'])
return super().update(instance, validated_data)

def to_representation(self, instance):
request = self.context.get('request')
context = {'request': request}
Expand Down
Loading