diff --git a/account/admin.py b/account/admin.py index e1decdd..7b59c84 100644 --- a/account/admin.py +++ b/account/admin.py @@ -11,6 +11,7 @@ class UserDeleteAdmin(admin.ModelAdmin): list_display = ['user', 'old_email', 'd_email'] def __str__(self): + """Return delete user""" return self.user @admin.register(UserNotification) class UserNotificationAdmin(admin.ModelAdmin): diff --git a/account/migrations/0006_alter_useremailotp_options_and_more.py b/account/migrations/0006_alter_useremailotp_options_and_more.py new file mode 100644 index 0000000..2321b9d --- /dev/null +++ b/account/migrations/0006_alter_useremailotp_options_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.2 on 2023-07-11 11:26 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0005_usernotification'), + ] + + operations = [ + migrations.AlterModelOptions( + name='useremailotp', + options={'verbose_name': 'User Email OTP', 'verbose_name_plural': 'User Email OTP'}, + ), + migrations.AlterModelOptions( + name='usernotification', + options={'verbose_name': 'User Notification', 'verbose_name_plural': 'User Notification'}, + ), + ] diff --git a/account/models.py b/account/models.py index 36b2286..60184c5 100644 --- a/account/models.py +++ b/account/models.py @@ -140,3 +140,4 @@ class UserNotification(models.Model): def __str__(self): return self.user.email + diff --git a/account/serializers.py b/account/serializers.py index 82b957e..88d544d 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -10,7 +10,7 @@ from junior.models import Junior from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp from base.constants import GUARDIAN, JUNIOR, SUPERUSER from base.messages import ERROR_CODE_REQUIRED, ERROR_CODE, SUCCESS_CODE, STATUS_CODE_ERROR -from .utils import junior_account_update, guardian_account_update +from .utils import delete_user_account_condition_social, delete_user_account_condition class GoogleLoginSerializer(serializers.Serializer): """google login serializer""" @@ -231,26 +231,17 @@ class UserDeleteSerializer(serializers.ModelSerializer): user_type = str(self.context['user_type']) data = validated_data.get('reason') passwd = self.context['password'] - random_num = random.randint(0,10000) + signup_method = self.context['signup_method'] + random_num = random.randint(0, 10000) user_tb = User.objects.filter(id=user.id).last() - if user_tb and user_tb.check_password(passwd): - user_type_data = UserEmailOtp.objects.filter(email=user.email).last() - if user_type == '1' and user_type_data.user_type == '1': - junior_account_update(user_tb) - elif user_type == '2' and user_type_data.user_type == '2': - guardian_account_update(user_tb) - else: - raise serializers.ValidationError({"details":ERROR_CODE['2030'],"code":"400", "status":"failed"}) - user_tb.email = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower() - user_tb.username = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower() - user_tb.password = 'None' - d_email = user_tb.email - o_mail = user.email - instance = UserDelete.objects.create(user=user_tb, d_email=d_email, old_email=o_mail, - is_active=True, reason=data) - user_tb.save() - + user_type_datas = UserEmailOtp.objects.filter(email=user.email).last() + if user_tb and user_tb.check_password(passwd) and signup_method == '1': + user_type_data = user_type_datas.user_type + instance = delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num) return instance + elif user_tb and passwd is None and signup_method in ['2','3']: + inst = delete_user_account_condition_social(user, user_type, user_tb, data, random_num) + return inst else: raise serializers.ValidationError({"details": ERROR_CODE['2031'], "code": "400", "status": "failed"}) diff --git a/account/templates/templated_email/support_mail.email b/account/templates/templated_email/support_mail.email new file mode 100644 index 0000000..50467a9 --- /dev/null +++ b/account/templates/templated_email/support_mail.email @@ -0,0 +1,22 @@ +{% extends "templated_email/email_base.email" %} + +{% block subject %} + {{subject}} +{% endblock %} + +{% block plain %} + + +

+ Hi {{name}}, +

+ + + + +

+ {{name}} have some queries and need some support. Please support them by using their email address {{sender}}.

Queries are:-
{{ message }} +

+ + +{% endblock %} diff --git a/account/urls.py b/account/urls.py index 6bfc8dd..f2a1d62 100644 --- a/account/urls.py +++ b/account/urls.py @@ -8,7 +8,7 @@ from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVer ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage, GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet, DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet, - UpdateUserNotificationAPIViewSet) + UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView) """Router""" router = routers.SimpleRouter() @@ -44,4 +44,6 @@ urlpatterns = [ path('api/v1/change-password/', ChangePasswordAPIView.as_view()), path('api/v1/update-profile-image/', UpdateProfileImage.as_view()), path('api/v1/apple-login/', SigninWithApple.as_view(), name='signup_with_apple'), + path('api/v1/send-support-email/', SendSupportEmail.as_view(), name='send-support-email'), + path('api/v1/logout/', LogoutAPIView.as_view(), name='logout') ] diff --git a/account/utils.py b/account/utils.py index af9f86b..17f1bf5 100644 --- a/account/utils.py +++ b/account/utils.py @@ -10,8 +10,50 @@ from datetime import datetime from calendar import timegm from uuid import uuid4 import secrets +from rest_framework import serializers from junior.models import Junior from guardian.models import Guardian +from account.models import UserDelete +from base.messages import ERROR_CODE + + +def delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num): + """delete user account""" + if user_type == '1' and user_type_data == '1': + junior_account_update(user_tb) + elif user_type == '2' and user_type_data == '2': + guardian_account_update(user_tb) + else: + raise serializers.ValidationError({"details": ERROR_CODE['2030'], "code": "400", "status": "failed"}) + user_tb.email = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower() + user_tb.username = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower() + user_tb.password = 'None' + d_email = user_tb.email + o_mail = user.email + user_tb.save() + instance = UserDelete.objects.create(user=user_tb, d_email=d_email, old_email=o_mail, + is_active=True, reason=data) + + return instance + +def delete_user_account_condition_social(user, user_type,user_tb, data, random_num): + """delete user account""" + if user_type == '1': + junior_account_update(user_tb) + elif user_type == '2': + guardian_account_update(user_tb) + else: + raise serializers.ValidationError({"details": ERROR_CODE['2030'], "code": "400", "status": "failed"}) + user_tb.email = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower() + user_tb.username = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower() + user_tb.password = 'None' + dummy_email = user_tb.email + old_mail = user.email + user_tb.save() + instance_data = UserDelete.objects.create(user=user_tb, d_email=dummy_email, old_email=old_mail, + is_active=True, reason=data) + + return instance_data def junior_account_update(user_tb): """junior account delete""" junior_data = Junior.objects.filter(auth__email=user_tb.email).first() @@ -46,6 +88,22 @@ def send_otp_email(recipient_email, otp): ) return otp +def send_support_email(name, sender, subject, message): + """Send otp on email with template""" + to_email = [settings.EMAIL_FROM_ADDRESS] + from_email = settings.DEFAULT_ADDRESS + send_templated_mail( + template_name='support_mail.email', + from_email=from_email, + recipient_list=to_email, + context={ + 'name': name.title(), + 'sender': sender, + 'subject': subject, + 'message': message + } + ) + return name def custom_response(detail, data=None, response_status=status.HTTP_200_OK): """Custom response code""" if not data: diff --git a/account/views.py b/account/views.py index b5c0d26..aab69fb 100644 --- a/account/views.py +++ b/account/views.py @@ -5,8 +5,10 @@ from rest_framework import viewsets, status, views from rest_framework.decorators import action import random import logging +from django.views.decorators.csrf import csrf_exempt from django.utils import timezone import jwt +from django.contrib.auth import logout """App Import""" from guardian.utils import upload_image_to_alibaba from django.contrib.auth import authenticate, login @@ -23,15 +25,14 @@ from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSeriali from rest_framework_simplejwt.tokens import RefreshToken from base.messages import ERROR_CODE, SUCCESS_CODE from guardian.tasks import generate_otp -from account.utils import send_otp_email -from account.utils import custom_response, custom_error_response +from account.utils import send_otp_email, send_support_email, custom_response, custom_error_response from rest_framework.permissions import IsAuthenticated from templated_email import send_templated_mail import google.oauth2.credentials import google.auth.transport.requests from rest_framework import status -from rest_framework.response import Response import requests +from rest_framework.response import Response from django.conf import settings from junior.serializers import JuniorProfileSerializer from guardian.serializers import GuardianProfileSerializer @@ -85,11 +86,11 @@ class GoogleLoginMixin: user_obj = User.objects.create(username=email, email=email, first_name=first_name, last_name=last_name) if str(user_type) == '1': junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True, - image=profile_picture) + image=profile_picture, signup_method='2') serializer = JuniorSerializer(junior_query) if str(user_type) == '2': guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True, - image=profile_picture) + image=profile_picture,signup_method='2') serializer = GuardianSerializer(guardian_query) # Return a JSON response with the user's email and name return custom_response(SUCCESS_CODE['3003'], serializer.data, @@ -97,9 +98,11 @@ class GoogleLoginMixin: class GoogleLoginViewSet(GoogleLoginMixin, viewsets.GenericViewSet): + """Google login viewset""" serializer_class = GoogleLoginSerializer def create(self, request): + """create method""" serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) return self.google_login(request) @@ -127,10 +130,12 @@ class SigninWithApple(views.APIView): except User.DoesNotExist: user = User.objects.create(**user_data) if str(user_type) == '1': - junior_query = Junior.objects.create(auth=user, is_verified=True, is_active=True) + junior_query = Junior.objects.create(auth=user, is_verified=True, is_active=True, + signup_method='3') serializer = JuniorSerializer(junior_query) if str(user_type) == '2': - guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True) + guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True, + signup_method='3') serializer = GuardianSerializer(guardian_query) return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK) @@ -140,6 +145,7 @@ class SigninWithApple(views.APIView): class UpdateProfileImage(views.APIView): + """Update profile image""" permission_classes = [IsAuthenticated] def put(self, request, format=None): if str(request.data['user_type']) == '1': @@ -164,6 +170,7 @@ class UpdateProfileImage(views.APIView): return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) class ChangePasswordAPIView(views.APIView): + """change password""" serializer_class = ChangePasswordSerializer permission_classes = [IsAuthenticated] def post(self, request): @@ -174,6 +181,7 @@ class ChangePasswordAPIView(views.APIView): return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) class ResetPasswordAPIView(views.APIView): + """Reset password""" def post(self, request): serializer = ResetPasswordSerializer(data=request.data) if serializer.is_valid(): @@ -182,6 +190,7 @@ class ResetPasswordAPIView(views.APIView): return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) class ForgotPasswordAPIView(views.APIView): + """Forgot password""" def post(self, request): serializer = ForgotPasswordSerializer(data=request.data) if serializer.is_valid(): @@ -253,6 +262,7 @@ class UserPhoneVerification(viewsets.ModelViewSet): class UserLogin(viewsets.ViewSet): + """User login""" @action(methods=['post'], detail=False) def login(self, request): username = request.data.get('username') @@ -287,6 +297,7 @@ class UserLogin(viewsets.ViewSet): email_verified.otp = otp email_verified.save() data.update({"email_otp":otp}) + send_otp_email(username, otp) return custom_response(ERROR_CODE['2024'], {"email_otp": otp, "is_email_verified": is_verified}, response_status=status.HTTP_200_OK) data.update({"is_email_verified": is_verified}) @@ -385,11 +396,13 @@ class ProfileAPIViewSet(viewsets.ModelViewSet): junior_data = Junior.objects.filter(auth=self.request.user).last() if junior_data: serializer = JuniorProfileSerializer(junior_data) - if str(self.request.GET.get('user_type')) == '2': + return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) + elif str(self.request.GET.get('user_type')) == '2': guardian_data = Guardian.objects.filter(user=self.request.user).last() if guardian_data: serializer = GuardianProfileSerializer(guardian_data) - return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) + return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) + class UploadImageAPIViewSet(viewsets.ModelViewSet): @@ -427,10 +440,13 @@ class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet): permission_classes=[IsAuthenticated]) def account(self, request): user_type = str(request.data['user_type']) - password = request.data['password'] + password = request.data.get('password') + signup_method = str(request.data.get('signup_method')) + print("signup_method===>",signup_method,'==>',type(signup_method)) serializer = self.get_serializer(data=request.data, context={'request': request, 'user': request.user, - 'user_type':user_type, - 'password':password}) + 'user_type': user_type, + 'password': password, + 'signup_method':signup_method}) if serializer.is_valid(): serializer.save() return custom_response(SUCCESS_CODE['3005'], response_status=status.HTTP_200_OK) @@ -461,3 +477,29 @@ class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet): serializer.save() return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST) + + +class SendSupportEmail(views.APIView): + """support email api""" + def post(self, request): + name = request.data.get('name') + sender = request.data.get('email') + subject = request.data.get('subject') + message = request.data.get('message') + if name and sender and subject and message: + try: + send_support_email(name, sender, subject, message) + return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK) + except Exception as e: + return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) + else: + return custom_error_response(ERROR_CODE['2033'], response_status=status.HTTP_400_BAD_REQUEST) + + +class LogoutAPIView(views.APIView): + permission_classes = (IsAuthenticated,) + + def post(self, request): + logout(request) + request.session.flush() + return custom_response(SUCCESS_CODE['3020'], response_status=status.HTTP_200_OK) diff --git a/base/constants.py b/base/constants.py index f455316..c1f48c3 100644 --- a/base/constants.py +++ b/base/constants.py @@ -26,15 +26,18 @@ sort_dict = { '1': 'name', '2': '-name' } +"""user type""" USER_TYPE = ( ('1', 'junior'), ('2', 'guardian'), ('3', 'superuser') ) +"""gender""" GENDERS = ( ('1', 'Male'), ('2', 'Female') ) +"""Task status""" TASK_STATUS = ( ('1', 'pending'), ('2', 'in-progress'), @@ -42,7 +45,12 @@ TASK_STATUS = ( ('4', 'requested'), ('5', 'completed') ) - +"""sign up method""" +SIGNUP_METHODS = ( + ('1', 'manual'), + ('2', 'google'), + ('3', 'apple') +) PENDING = 1 IN_PROGRESS = 2 REJECTED = 3 diff --git a/base/messages.py b/base/messages.py index 788c7bc..7810bfc 100644 --- a/base/messages.py +++ b/base/messages.py @@ -54,7 +54,9 @@ ERROR_CODE = { "2028": "You are not authorized person to sign up on this platform", "2029": "Validity of otp verification is expired", "2030": "Use correct user type and token", - "2031":"Invalid password" + "2031": "Invalid password", + "2032": "Failed to send email", + "2033": "Missing required fields" } SUCCESS_CODE = { # Success code for password @@ -84,7 +86,9 @@ SUCCESS_CODE = { "3015": "Verification code sent on your email.", "3016": "Send otp on your Email successfully", "3017": "Profile image update successfully", - "3018": "Task created successfully" + "3018": "Task created successfully", + "3019": "Support Email sent successfully", + "3020": "Logged out successfully." } STATUS_CODE_ERROR = { diff --git a/guardian/migrations/0014_guardian_signup_method.py b/guardian/migrations/0014_guardian_signup_method.py new file mode 100644 index 0000000..c0c1fe7 --- /dev/null +++ b/guardian/migrations/0014_guardian_signup_method.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-07-11 11:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('guardian', '0013_alter_guardian_image'), + ] + + operations = [ + migrations.AddField( + model_name='guardian', + name='signup_method', + field=models.CharField(choices=[('1', 'manual'), ('2', 'google'), ('3', 'apple')], default='1', max_length=31), + ), + ] diff --git a/guardian/models.py b/guardian/models.py index 1bb4d2c..921138a 100644 --- a/guardian/models.py +++ b/guardian/models.py @@ -3,7 +3,7 @@ from django.db import models from django.contrib.auth import get_user_model """Import Django app""" -from base.constants import GENDERS, TASK_STATUS, PENDING, TASK_POINTS +from base.constants import GENDERS, TASK_STATUS, PENDING, TASK_POINTS, SIGNUP_METHODS from junior.models import Junior """Add user model""" User = get_user_model() @@ -27,6 +27,8 @@ class Guardian(models.Model): is_verified = models.BooleanField(default=False) is_complete_profile = models.BooleanField(default=False) passcode = models.IntegerField(null=True, blank=True, default=None) + """Sign up method""" + signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1') """Codes""" guardian_code = models.CharField(max_length=10, null=True, blank=True, default=None) referral_code = models.CharField(max_length=10, null=True, blank=True, default=None) diff --git a/guardian/serializers.py b/guardian/serializers.py index 2c1c3dc..9ba837f 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -173,6 +173,7 @@ class GuardianDetailSerializer(serializers.ModelSerializer): 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'updated_at'] class TaskDetailsSerializer(serializers.ModelSerializer): + """Task detail serializer""" junior = JuniorDetailSerializer() class Meta(object): @@ -187,7 +188,7 @@ class TopJuniorSerializer(serializers.ModelSerializer): junior = JuniorDetailSerializer() position = serializers.IntegerField() - class Meta: + class Meta(object): """Meta info""" model = JuniorPoints fields = ['id', 'junior', 'total_task_points', 'position', 'created_at', 'updated_at'] @@ -241,5 +242,5 @@ class GuardianProfileSerializer(serializers.ModelSerializer): model = Guardian fields = ['id', 'email', 'first_name', 'last_name', 'country_name','country_code', 'phone', 'gender', 'dob', 'guardian_code', 'notification_count', 'total_count', 'complete_field_count', 'referral_code', - 'is_active', 'is_complete_profile', 'created_at', 'image', - 'updated_at'] + 'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method', + 'updated_at', 'passcode'] diff --git a/guardian/utils.py b/guardian/utils.py index ff8472c..f565e4b 100644 --- a/guardian/utils.py +++ b/guardian/utils.py @@ -1,7 +1,9 @@ """Utiles file of guardian""" """Django import""" import oss2 +"""Import setting""" from django.conf import settings +"""Import tempfile""" import tempfile def upload_image_to_alibaba(image, filename): diff --git a/guardian/views.py b/guardian/views.py index f55dd47..f8295a3 100644 --- a/guardian/views.py +++ b/guardian/views.py @@ -93,7 +93,7 @@ class TaskListAPIView(viewsets.ModelViewSet): """Create guardian profile""" status_value = self.request.GET.get('status') if str(status_value) == '0': - queryset = JuniorTask.objects.filter(guardian__user=request.user).order_by('created_at') + queryset = JuniorTask.objects.filter(guardian__user=request.user).order_by('due_date', 'created_at') else: queryset = JuniorTask.objects.filter(guardian__user=request.user, task_status=status_value).order_by('due_date','created_at') diff --git a/junior/migrations/0010_junior_signup_method.py b/junior/migrations/0010_junior_signup_method.py new file mode 100644 index 0000000..a87d77a --- /dev/null +++ b/junior/migrations/0010_junior_signup_method.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-07-11 11:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0009_juniorpoints_position'), + ] + + operations = [ + migrations.AddField( + model_name='junior', + name='signup_method', + field=models.CharField(choices=[('1', 'manual'), ('2', 'google'), ('3', 'apple')], default='1', max_length=31), + ), + ] diff --git a/junior/models.py b/junior/models.py index 505a144..c150d20 100644 --- a/junior/models.py +++ b/junior/models.py @@ -4,7 +4,7 @@ from django.db import models from django.contrib.auth import get_user_model from django.contrib.postgres.fields import ArrayField """Import django app""" -from base.constants import GENDERS +from base.constants import GENDERS, SIGNUP_METHODS User = get_user_model() # Create your models here. @@ -19,6 +19,8 @@ class Junior(models.Model): gender = models.CharField(max_length=10, choices=GENDERS, null=True, blank=True, default=None) dob = models.DateField(max_length=15, null=True, blank=True, default=None) image = models.URLField(null=True, blank=True, default=None) + """Sign up method""" + signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1') """Codes""" junior_code = models.CharField(max_length=10, null=True, blank=True, default=None) guardian_code = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None),null=True) diff --git a/junior/serializers.py b/junior/serializers.py index df7a40a..cf936cb 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -237,4 +237,5 @@ class JuniorProfileSerializer(serializers.ModelSerializer): model = Junior fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', - 'updated_at', 'notification_count', 'total_count', 'complete_field_count'] + 'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method', + 'passcode'] diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 56bb80d..f7b79dc 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -154,6 +154,10 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] +JWT_AUTH = { + # Other JWT authentication settings + 'JWT_AUTHENTICATION': 'your_app.authentication.CustomJWTAuthentication', +} # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ @@ -167,7 +171,6 @@ USE_I18N = True USE_L10N = True USE_TZ = True - # cors header settings SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') @@ -209,6 +212,7 @@ EMAIL_USE_TLS="True" EMAIL_HOST_USER="apikey" EMAIL_HOST_PASSWORD="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" EMAIL_FROM_ADDRESS="support@zodbank.com" +DEFAULT_ADDRESS="zodbank@yopmail.com"