diff --git a/.github/workflows/deploy-bot-on-prod.yml b/.github/workflows/deploy-bot-on-prod.yml index 547e4484..8efd1c94 100644 --- a/.github/workflows/deploy-bot-on-prod.yml +++ b/.github/workflows/deploy-bot-on-prod.yml @@ -13,13 +13,19 @@ jobs: name: prod_deploy if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - name: copy service file + - uses: actions/checkout@v2 + - name: Prepare infra/prod + run: | + mkdir ../build + cp -TR ./infra/prod ../build + tar -cvf deploy.tar ../build/ + - name: copy infra/prod uses: appleboy/scp-action@v0.1.4 with: host: ${{ secrets.VM_HOST }} username: ${{ secrets.VM_USER }} password: ${{ secrets.VM_PASSWORD }} - source: "infra/prod/" + source: "deploy.tar" target: /home/deploy/spread_wings_bot/infra/prod/ - name: ssh pull and start uses: appleboy/ssh-action@master @@ -29,6 +35,8 @@ jobs: password: ${{ secrets.VM_PASSWORD }} script: | cd /home/deploy/spread_wings_bot/infra/prod/ + tar -xvf deploy.tar --strip-components 1 + rm deploy.tar rm .env touch .env @@ -69,16 +77,14 @@ jobs: docker system prune --force # Installing defend service for app - # Шаг с копированием в строках 16-23 можно заменить командой ниже - нужно тестировать - # scp infra/prod/spread_wings_bot.service ${{ secrets.VM_USER }}@${{ secrets.VM_HOST }}:/spread_wings_bot/infra/prod/ - sudo cp -f /home/deploy/spread_wings_bot/infra/prod/spread_wings_bot.service /etc/systemd/system/spread_wings_bot.service + sudo cp -f /home/deploy/spread_wings_bot/infra/prod/spread_wings_bot_prod.service /etc/systemd/system/spread_wings_bot_prod.service sudo systemctl daemon-reload - sudo systemctl restart spread_wings_bot.service + sudo systemctl restart spread_wings_bot_prod.service # Installing the app - docker-compose -f docker-compose.stage.yaml stop - docker-compose -f docker-compose.stage.yaml pull - docker-compose -f docker-compose.stage.yaml up -d + docker-compose -f docker-compose.prod.yaml stop + docker-compose -f docker-compose.prod.yaml pull + docker-compose -f docker-compose.prod.yaml up -d # Applying initialization commands docker exec spread-wings-bot python manage.py migrate diff --git a/Makefile b/Makefile index 6c173211..ed82e952 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ help: # Show help .PHONY: runbot-init -runbot-init: deletedb rundb migrate filldb collectstatic createsuperuser runbot-db # Build and run Database Docker-image +runbot-init: deletedb rundb migrate filldb collectstatic runbot-db # Build and run Database Docker-image @echo -e "$(COLOR_YELLOW)Starting initialization...$(COLOR_RESET)" @source $$(poetry env info -p)/bin/activate diff --git a/infra/dev/nginx.stage.conf.template b/infra/dev/nginx.stage.conf.template index f6a7bbd1..f3c1f1d9 100644 --- a/infra/dev/nginx.stage.conf.template +++ b/infra/dev/nginx.stage.conf.template @@ -27,6 +27,14 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } + location /users/ { + proxy_pass http://spread-wings-bot:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + error_page 500 502 503 504 /50x.html; location = /50x.html { diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index b3e20a05..15d4ba98 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -60,7 +60,7 @@ services: ports: - "80:${NGINX_PORT}" volumes: - - ./nginx.stage.conf.template:/etc/nginx/templates/default.conf.template + - ./nginx.prod.conf.template:/etc/nginx/templates/default.conf.template - ../../static:/var/html/static/ - ../../media:/var/html/media/ - ./logs/nginx/:/var/log/nginx/ diff --git a/infra/prod/nginx.prod.conf.template b/infra/prod/nginx.prod.conf.template index ea1449a0..ac25d900 100644 --- a/infra/prod/nginx.prod.conf.template +++ b/infra/prod/nginx.prod.conf.template @@ -27,6 +27,13 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } + location /users/ { + proxy_pass http://spread-wings-bot:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } error_page 500 502 503 504 /50x.html; diff --git a/infra/prod/spread_wings_bot.service b/infra/prod/spread_wings_bot_prod.service similarity index 100% rename from infra/prod/spread_wings_bot.service rename to infra/prod/spread_wings_bot_prod.service diff --git a/src/config/settings.py b/src/config/settings.py index c65ab9c0..b991ac10 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -116,7 +116,7 @@ EMAIL_BACKEND = env.str( "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" ) -EMAIL_TEMPLATE_NAME = "email.html" +EMAIL_TEMPLATE_NAME = "emailing/email.html" EMAIL_HOST = env.str("EMAIL_HOST") try: EMAIL_PORT = env.int("EMAIL_PORT") diff --git a/src/users/admin.py b/src/users/admin.py index fe4a11e6..969acb28 100644 --- a/src/users/admin.py +++ b/src/users/admin.py @@ -2,6 +2,7 @@ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.models import Group, Permission from django.utils.translation import gettext_lazy as _ +from django_otp.plugins.otp_email.models import EmailDevice from .forms import UserChangeForm, UserCreationForm from .models import User @@ -80,6 +81,17 @@ def reset_password(self, request, queryset): admin.site.unregister(Group) +class OTPDevice(admin.ModelAdmin): + """Empty class to hide OTPDevice model.""" + + def get_model_perms(self, request): + """Return empty perms dict thus hiding the model from admin.""" + return {} + + +admin.site.register(EmailDevice, OTPDevice) + + def permissions_new_unicode(self): """Translate default permissions.""" class_name = str(self.content_type) diff --git a/src/users/forms.py b/src/users/forms.py index e15f740b..b4327408 100644 --- a/src/users/forms.py +++ b/src/users/forms.py @@ -1,6 +1,7 @@ from django import forms from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.hashers import make_password +from django.db import transaction from django.utils.translation import gettext_lazy as _ from django.utils.translation import ngettext_lazy from django_otp.forms import OTPAuthenticationFormMixin @@ -57,6 +58,43 @@ class CustomOTPAuthenticationFormMixin(OTPAuthenticationFormMixin): "verification_not_allowed": _("Верификация недоступна"), } + def clean_otp(self, user): + """Perform OTP clean.""" + if user is None: + return + + validation_error = None + with transaction.atomic(): + try: + device = self._chosen_device(user) + token = self.cleaned_data.get("otp_token") + + user.otp_device = None + + try: + if self.cleaned_data.get("otp_challenge"): + self._handle_challenge(device) + elif token: + user.otp_device = self._verify_token( + user, token, device + ) + else: + self._handle_challenge(device) + raise forms.ValidationError( + self.otp_error_messages["token_required"], + code="token_required", + ) + finally: + if user.otp_device is None: + self._update_form(user) + except forms.ValidationError as e: + # Validation errors shouldn't abort the transaction, so we have + # to carefully transport them out. + validation_error = e + + if validation_error: + raise validation_error + def _chosen_device(self, user): """Return EmailDevise as default.""" return EmailDevice.objects.filter(user=user).first() diff --git a/src/users/templates/authentication/login.html b/src/users/templates/authentication/login.html index 1b911aa7..e7744288 100644 --- a/src/users/templates/authentication/login.html +++ b/src/users/templates/authentication/login.html @@ -83,7 +83,7 @@
{% if form.get_user %} - + {% endif %}
diff --git a/src/users/templates/emailing/email.html b/src/users/templates/emailing/email.html index babd523d..2b9cea24 100644 --- a/src/users/templates/emailing/email.html +++ b/src/users/templates/emailing/email.html @@ -1,7 +1,6 @@ - + @@ -57,32 +56,18 @@ margin: 13px 0; } </style> - <!--[if !mso]><!--> + <style type="text/css"> @media only screen and (max-width:480px) { @-ms-viewport { width: 320px; } + @viewport { width: 320px; } } </style> - <!--<![endif]--> - <!--[if mso]> - <xml> - <o:OfficeDocumentSettings> - <o:AllowPNG/> - <o:PixelsPerInch>96</o:PixelsPerInch> - </o:OfficeDocumentSettings> - </xml> - <![endif]--> - <!--[if lte mso 11]> - <style type="text/css"> - .outlook-group-fix { width:100% !important; } - </style> - <![endif]--> - <style type="text/css"> @media only screen and (min-width:480px) { @@ -101,295 +86,194 @@ <body style="background-color:#f9f9f9;"> -<div style="background-color:#f9f9f9;"> - - - <!--[if mso | IE]> - <table - align="center" border="0" cellpadding="0" cellspacing="0" - style="width:600px;" width="600" - > - <tr> - <td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"> - <![endif]--> - - - <div style="background:#f9f9f9;background-color:#f9f9f9;Margin:0px auto;max-width:600px;"> - - <table align="center" border="0" cellpadding="0" cellspacing="0" - role="presentation" - style="background:#f9f9f9;background-color:#f9f9f9;width:100%;"> - <tbody> - <tr> - <td style="border-bottom:#333957 solid 5px;direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"> - <!--[if mso | IE]> - <table role="presentation" border="0" cellpadding="0" - cellspacing="0"> - - <tr> - - </tr> - - </table> - <![endif]--> - </td> - </tr> - </tbody> - </table> + <div style="background-color:#f9f9f9;"> - </div> - - - <!--[if mso | IE]> - </td> - </tr> - </table> - - <table - align="center" border="0" cellpadding="0" cellspacing="0" - style="width:600px;" width="600" - > - <tr> - <td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"> - <![endif]--> - - - <div style="background:#fff;background-color:#fff;Margin:0px auto;max-width:600px;"> - - <table align="center" border="0" cellpadding="0" cellspacing="0" - role="presentation" - style="background:#fff;background-color:#fff;width:100%;"> - <tbody> - <tr> - <td style="border:#dddddd solid 1px;border-top:0px;direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"> - <!--[if mso | IE]> - <table role="presentation" border="0" cellpadding="0" - cellspacing="0"> + <div style="background:#f9f9f9;background-color:#f9f9f9;Margin:0px auto;max-width:600px;"> - <tr> - - <td - style="vertical-align:bottom;width:600px;" - > - <![endif]--> + <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" + style="background:#f9f9f9;background-color:#f9f9f9;width:100%;"> + <tbody> + <tr> + <td + style="border-bottom:#333957 solid 5px;direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"> + </td> + </tr> + </tbody> + </table> - <div class="mj-column-per-100 outlook-group-fix" - style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:bottom;width:100%;"> + </div> - <table border="0" cellpadding="0" cellspacing="0" - role="presentation" - style="vertical-align:bottom;" width="100%"> + <div style="background:#fff;background-color:#fff;Margin:0px auto;max-width:600px;"> - <tr> - <td align="center" - style="font-size:0px;padding:10px 25px;word-break:break-word;"> + <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" + style="background:#fff;background-color:#fff;width:100%;"> + <tbody> + <tr> + <td + style="border:#dddddd solid 1px;border-top:0px;direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"> + <div class="mj-column-per-100 outlook-group-fix" + style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:bottom;width:100%;"> - <table align="center" border="0" - cellpadding="0" cellspacing="0" - role="presentation" - style="border-collapse:collapse;border-spacing:0px;"> - <tbody> - <tr> - <td style="width:64px;"> + <table border="0" cellpadding="0" cellspacing="0" role="presentation" + style="vertical-align:bottom;" width="100%"> - <img height="auto" - src="https://detskyfond.info/wp-content/themes/detskyfond/wp-images/logo.png" - style="border:0;display:block;outline:none;text-decoration:none;width:100%;" - width="64"/> + <tr> + <td align="center" + style="font-size:0px;padding:10px 25px;word-break:break-word;"> - </td> - </tr> - </tbody> - </table> - - </td> - </tr> - - <tr> - <td align="center" - style="font-size:0px;padding:10px 25px;padding-bottom:40px;word-break:break-word;"> - <div style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:28px;font-weight:bold;line-height:1;text-align:center;color:#555;"> - {% block subject %} - {% endblock %} - </div> - </td> - </tr> - - <tr> - <td align="left" - style="font-size:0px;padding:10px 25px;word-break:break-word;"> - <div style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:16px;line-height:22px;text-align:left;color:#555;"> - {% block content %} - {% endblock %} - </div> - - </td> - </tr> - - <tr> - <td align="center" - style="font-size:0px;padding:10px 25px;padding-top:30px;padding-bottom:50px;word-break:break-word;"> - - <table align="center" border="0" - cellpadding="0" cellspacing="0" - role="presentation" - style="border-collapse:separate;line-height:100%;"> - <tr> - <td align="center" - bgcolor="#2F67F6" + <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" - style="border:none;border-radius:3px;color:#ffffff;cursor:auto;padding:15px 25px;" - valign="middle"> - <a href="https://detskyfond.info" - style="background:#2F67F6;color:#ffffff;font-family:'Helvetica Neue',Arial,sans-serif;font-size:15px;font-weight:normal;line-height:120%;Margin:0;text-decoration:none;text-transform:none;"> - Перейти на сайт - </a> - </td> - </tr> - </table> - - </td> - </tr> - - <tr> - <td align="left" - style="font-size:0px;padding:10px 25px;word-break:break-word;"> - - <div style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:14px;line-height:20px;text-align:left;color:#525252;"> - С наилучшими пожеланиями, - <br><br> - Благотворительный фонд социальной - помощи детям <a - href="https://detskyfond.info" - style="color:#2F67F6">«Расправь - крылья»</a> - </div> - - </td> - </tr> - - </table> - - </div> - - <!--[if mso | IE]> - </td> - + style="border-collapse:collapse;border-spacing:0px;"> + <tbody> + <tr> + <td style="width:64px;"> + + <img height="auto" + src="https://detskyfond.info/wp-content/themes/detskyfond/wp-images/logo.png" + style="border:0;display:block;outline:none;text-decoration:none;width:100%;" + width="64" /> + + </td> + </tr> + </tbody> + </table> + + </td> + </tr> + + <tr> + <td align="center" + style="font-size:0px;padding:10px 25px;padding-bottom:40px;word-break:break-word;"> + <div + style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:28px;font-weight:bold;line-height:1;text-align:center;color:#555;"> + {% block subject %} + {{subject}} + {% endblock %} + </div> + </td> + </tr> + + <tr> + <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> + <div + style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:16px;line-height:22px;text-align:left;color:#555;"> + {% block content %} + {{message|linebreaks}} + {% endblock %} + </div> + + </td> + </tr> + + <tr> + <td align="center" + style="font-size:0px;padding:10px 25px;padding-top:30px;padding-bottom:50px;word-break:break-word;"> + + <table align="center" border="0" cellpadding="0" cellspacing="0" + role="presentation" style="border-collapse:separate;line-height:100%;"> + <tr> + <td align="center" bgcolor="#2F67F6" role="presentation" + style="border:none;border-radius:3px;color:#ffffff;cursor:auto;padding:15px 25px;" + valign="middle"> + <a href="https://detskyfond.info" + style="background:#2F67F6;color:#ffffff;font-family:'Helvetica Neue',Arial,sans-serif;font-size:15px;font-weight:normal;line-height:120%;Margin:0;text-decoration:none;text-transform:none;"> + Перейти на сайт + </a> + </td> + </tr> + </table> + + </td> + </tr> + + <tr> + <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> + + <div + style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:14px;line-height:20px;text-align:left;color:#525252;"> + С наилучшими пожеланиями, + <br><br> + Благотворительный фонд социальной + помощи детям <a href="https://detskyfond.info" + style="color:#2F67F6">«Расправь + крылья!»</a> + </div> + + </td> + </tr> + + </table> + + </div> + + </td> </tr> + </tbody> + </table> - </table> - <![endif]--> - </td> - </tr> - </tbody> - </table> - - </div> - - - <!--[if mso | IE]> - </td> - </tr> - </table> - - <table - align="center" border="0" cellpadding="0" cellspacing="0" - style="width:600px;" width="600" - > - <tr> - <td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"> - <![endif]--> + </div> + <div style="Margin:0px auto;max-width:600px;"> - <div style="Margin:0px auto;max-width:600px;"> + <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;"> + <tbody> + <tr> + <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"> - <table align="center" border="0" cellpadding="0" cellspacing="0" - role="presentation" style="width:100%;"> - <tbody> - <tr> - <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;vertical-align:top;"> - <!--[if mso | IE]> - <table role="presentation" border="0" cellpadding="0" - cellspacing="0"> + <div class="mj-column-per-100 outlook-group-fix" + style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:bottom;width:100%;"> - <tr> + <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%"> + <tbody> + <tr> + <td style="vertical-align:bottom;padding:0;"> - <td - style="vertical-align:bottom;width:600px;" - > - <![endif]--> + <table border="0" cellpadding="0" cellspacing="0" role="presentation" + width="100%"> - <div class="mj-column-per-100 outlook-group-fix" - style="font-size:13px;text-align:left;direction:ltr;display:inline-block;vertical-align:bottom;width:100%;"> + <tr> + <td align="center" + style="font-size:0px;padding:0;word-break:break-word;"> - <table border="0" cellpadding="0" cellspacing="0" - role="presentation" width="100%"> - <tbody> - <tr> - <td style="vertical-align:bottom;padding:0;"> + <div + style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:12px;font-weight:300;line-height:1;text-align:center;color:#575757;"> + Тел. 8 (800) 101-29-80 + </div> - <table border="0" cellpadding="0" - cellspacing="0" - role="presentation" - width="100%"> + </td> + </tr> - <tr> - <td align="center" - style="font-size:0px;padding:0;word-break:break-word;"> + <tr> + <td align="center" + style="font-size:0px;padding:10px;word-break:break-word;"> - <div style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:12px;font-weight:300;line-height:1;text-align:center;color:#575757;"> - Тел. 8 (800) 101-29-80 - </div> + <div + style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:12px;font-weight:300;line-height:1;text-align:center;color:#575757;"> + <a href="#" style="color:#575757">Отписаться</a> + от рассылки + </div> - </td> - </tr> + </td> + </tr> - <tr> - <td align="center" - style="font-size:0px;padding:10px;word-break:break-word;"> - - <div style="font-family:'Helvetica Neue',Arial,sans-serif;font-size:12px;font-weight:300;line-height:1;text-align:center;color:#575757;"> - <a href="#" - style="color:#575757">Отписаться</a> - от рассылки - </div> + </table> </td> </tr> + </tbody> + </table> - </table> - - </td> - </tr> - </tbody> - </table> - - </div> - - <!--[if mso | IE]> - </td> + </div> + </td> </tr> + </tbody> + </table> - </table> - <![endif]--> - </td> - </tr> - </tbody> - </table> + </div> </div> - - <!--[if mso | IE]> - </td> - </tr> - </table> - <![endif]--> - - -</div> - </body> </html>