diff --git a/apps/authorization/serializers/common.py b/apps/authorization/serializers/common.py index 6ee108dd..b9eeb8c7 100644 --- a/apps/authorization/serializers/common.py +++ b/apps/authorization/serializers/common.py @@ -81,7 +81,6 @@ class LoginByUsernameOrEmailSerializer(SourceSerializerMixin, """Serializer for login user""" # REQUEST username_or_email = serializers.CharField(write_only=True) - password = serializers.CharField(write_only=True) # For cookie properties (Max-Age) remember = serializers.BooleanField(write_only=True) @@ -101,21 +100,24 @@ class LoginByUsernameOrEmailSerializer(SourceSerializerMixin, 'refresh_token', 'access_token', ) + extra_kwargs = { + 'password': {'write_only': True} + } def validate(self, attrs): """Override validate method""" username_or_email = attrs.pop('username_or_email') password = attrs.pop('password') user_qs = account_models.User.objects.filter(Q(username=username_or_email) | - (Q(email=username_or_email))) + Q(email=username_or_email)) if not user_qs.exists(): - raise utils_exceptions.UserNotFoundError() + raise utils_exceptions.WrongAuthCredentials() else: user = user_qs.first() authentication = authenticate(username=user.get_username(), password=password) if not authentication: - raise utils_exceptions.UserNotFoundError() + raise utils_exceptions.WrongAuthCredentials() self.instance = user return attrs @@ -127,10 +129,6 @@ class LoginByUsernameOrEmailSerializer(SourceSerializerMixin, return super().to_representation(instance) -class LogoutSerializer(SourceSerializerMixin): - """Serializer for Logout endpoint.""" - - class RefreshTokenSerializer(SourceSerializerMixin): """Serializer for refresh token view""" refresh_token = serializers.CharField(read_only=True) @@ -169,7 +167,3 @@ class RefreshTokenSerializer(SourceSerializerMixin): class OAuth2Serialzier(SourceSerializerMixin): """Serializer OAuth2 authorization""" token = serializers.CharField(max_length=255) - - -class OAuth2LogoutSerializer(SourceSerializerMixin): - """Serializer for logout""" diff --git a/apps/authorization/views/common.py b/apps/authorization/views/common.py index bb337dce..0b1a58e0 100644 --- a/apps/authorization/views/common.py +++ b/apps/authorization/views/common.py @@ -27,24 +27,6 @@ from utils.permissions import IsAuthenticatedAndTokenIsValid from utils.views import JWTGenericViewMixin -# Mixins -# JWTAuthView mixin -class JWTAuthViewMixin(JWTGenericViewMixin): - """Mixin for authentication views""" - - def post(self, request, *args, **kwargs): - """Implement POST method""" - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - response = Response(serializer.data, status=status.HTTP_200_OK) - access_token = serializer.data.get('access_token') - refresh_token = serializer.data.get('refresh_token') - return self._put_cookies_in_response( - cookies=self._put_data_in_cookies(access_token=access_token, - refresh_token=refresh_token), - response=response) - - # OAuth2 class BaseOAuth2ViewMixin(generics.GenericAPIView): """BaseMixin for classic auth views""" @@ -112,6 +94,7 @@ class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin): # Preparing request data serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) + request_data = self.prepare_request_data(serializer.validated_data) source = serializer.validated_data.get('source') request_data.update({ @@ -196,7 +179,7 @@ class ConfirmationEmailView(JWTGenericViewMixin): # Login by username|email + password -class LoginByUsernameOrEmailView(JWTAuthViewMixin): +class LoginByUsernameOrEmailView(JWTGenericViewMixin): """Login by email and password""" permission_classes = (permissions.AllowAny,) serializer_class = serializers.LoginByUsernameOrEmailSerializer @@ -205,10 +188,12 @@ class LoginByUsernameOrEmailView(JWTAuthViewMixin): """Implement POST method""" serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) + response = Response(serializer.data, status=status.HTTP_200_OK) access_token = serializer.data.get('access_token') refresh_token = serializer.data.get('refresh_token') is_permanent = serializer.validated_data.get('remember') + return self._put_cookies_in_response( cookies=self._put_data_in_cookies(access_token=access_token, refresh_token=refresh_token, @@ -243,9 +228,11 @@ class RefreshTokenView(JWTGenericViewMixin): def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) + response = Response(serializer.data, status=status.HTTP_201_CREATED) access_token = serializer.data.get('access_token') refresh_token = serializer.data.get('refresh_token') + return self._put_cookies_in_response( cookies=self._put_data_in_cookies(access_token=access_token, refresh_token=refresh_token), diff --git a/apps/news/admin.py b/apps/news/admin.py index 0bb4c8cc..61754a8f 100644 --- a/apps/news/admin.py +++ b/apps/news/admin.py @@ -1,7 +1,8 @@ from django.contrib import admin +from django.conf import settings from news import models - +from .tasks import send_email_with_news @admin.register(models.NewsType) class NewsTypeAdmin(admin.ModelAdmin): @@ -10,9 +11,21 @@ class NewsTypeAdmin(admin.ModelAdmin): list_display_links = ['id', 'name'] +def send_email_action(modeladmin, request, queryset): + news_ids = list(queryset.values_list("id", flat=True)) + if settings.USE_CELERY: + send_email_with_news.delay(news_ids) + else: + send_email_with_news(news_ids) + + +send_email_action.short_description = "Send the selected news by email" + + @admin.register(models.News) class NewsAdmin(admin.ModelAdmin): """News admin.""" + actions = [send_email_action] @admin.register(models.NewsGallery) diff --git a/apps/news/tasks.py b/apps/news/tasks.py new file mode 100644 index 00000000..99065fc8 --- /dev/null +++ b/apps/news/tasks.py @@ -0,0 +1,28 @@ +from celery import shared_task +from django.core.mail import send_mail +from notification.models import Subscriber +from news import models +from django.template.loader import render_to_string +from django.conf import settings +from smtplib import SMTPException + + +@shared_task +def send_email_with_news(news_ids): + + subscribers = Subscriber.objects.filter(state=Subscriber.USABLE) + sent_news = models.News.objects.filter(id__in=news_ids) + + for s in subscribers: + try: + for n in sent_news: + send_mail("G&M News", render_to_string(settings.NEWS_EMAIL_TEMPLATE, + {"title": n.title.get(s.locale), + "subtitle": n.subtitle.get(s.locale), + "description": n.description.get(s.locale), + "code": s.update_code, + "domain_uri": settings.DOMAIN_URI, + "country_code": s.country_code}), + settings.EMAIL_HOST_USER, [s.send_to], fail_silently=False) + except SMTPException: + continue diff --git a/apps/notification/urls/common.py b/apps/notification/urls/common.py index df43c805..842aa642 100644 --- a/apps/notification/urls/common.py +++ b/apps/notification/urls/common.py @@ -2,6 +2,7 @@ from django.urls import path from notification.views import common +app_name = "notification" urlpatterns = [ path('subscribe/', common.SubscribeView.as_view(), name='subscribe'), diff --git a/apps/utils/exceptions.py b/apps/utils/exceptions.py index df9c2c27..440f4ed4 100644 --- a/apps/utils/exceptions.py +++ b/apps/utils/exceptions.py @@ -123,7 +123,7 @@ class WrongAuthCredentials(AuthErrorMixin): """ The exception should be raised when credentials is not valid for this user """ - default_detail = _('Wrong authorization credentials') + default_detail = _('Incorrect login or password.') class FavoritesError(exceptions.APIException): diff --git a/project/settings/base.py b/project/settings/base.py index 827b78c6..063a5090 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -272,9 +272,9 @@ SMS_SENDER = 'GM' # EMAIL EMAIL_USE_TLS = True -EMAIL_HOST = 'smtp.gmail.com' -EMAIL_HOST_USER = 'anatolyfeteleu@gmail.com' -EMAIL_HOST_PASSWORD = 'nggrlnbehzksgmbt' +EMAIL_HOST = 'smtp.yandex.ru' +EMAIL_HOST_USER = 't3st.t3stov.t3stovich@yandex.ru' +EMAIL_HOST_PASSWORD = 'ylhernyutkfbylgk' EMAIL_PORT = 587 @@ -397,6 +397,7 @@ PASSWORD_RESET_TIMEOUT_DAYS = 1 RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html' CHANGE_EMAIL_TEMPLATE = 'account/change_email.html' CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_email.html' +NEWS_EMAIL_TEMPLATE = "news/news_email.html" # COOKIES diff --git a/project/templates/news/news_email.html b/project/templates/news/news_email.html new file mode 100644 index 00000000..a47af685 --- /dev/null +++ b/project/templates/news/news_email.html @@ -0,0 +1,20 @@ + + +
+ +{{ description }}
+ +https://{{ country_code }}.{{ domain_uri }}{% url 'web:notification:unsubscribe' code %} + + + + diff --git a/project/urls/web.py b/project/urls/web.py index 0a81672d..5bf538f3 100644 --- a/project/urls/web.py +++ b/project/urls/web.py @@ -23,7 +23,7 @@ urlpatterns = [ path('collections/', include('collection.urls.web')), path('establishments/', include('establishment.urls.web')), path('news/', include('news.urls.web')), - path('notifications/', include('notification.urls.web')), + path('notifications/', include(('notification.urls.web', "notification"), namespace='notification')), path('partner/', include('partner.urls.web')), path('location/', include('location.urls.web')), path('main/', include('main.urls')),