diff --git a/akarpov/blog/migrations/0002_alter_comment_options.py b/akarpov/blog/migrations/0002_alter_comment_options.py new file mode 100644 index 0000000..b4aa79f --- /dev/null +++ b/akarpov/blog/migrations/0002_alter_comment_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.0.8 on 2022-11-23 09:23 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='comment', + options={'ordering': ['-rating', '-created']}, + ), + ] diff --git a/akarpov/blog/models.py b/akarpov/blog/models.py index c4f9177..7017119 100644 --- a/akarpov/blog/models.py +++ b/akarpov/blog/models.py @@ -98,7 +98,7 @@ def __str__(self): return f"{self.author.username}'s comment on {self.post.title}" class Meta: - ordering = ["-rating"] + ordering = ["-rating", "-created"] class CommentRating(models.Model): diff --git a/akarpov/blog/signals.py b/akarpov/blog/signals.py index 4579e52..143da5c 100644 --- a/akarpov/blog/signals.py +++ b/akarpov/blog/signals.py @@ -19,7 +19,14 @@ def post_on_save(sender, instance: Post, **kwargs): {"image_cropped"} ): if instance.image: - crop_model_image.delay(instance.pk, "blog", "Post") + crop_model_image.apply_async( + kwargs={ + "pk": instance.pk, + "app_label": "blog", + "model_name": "Post", + }, + countdown=10, + ) else: instance.image_cropped = None instance.save() @@ -74,4 +81,11 @@ def post_rating_delete(sender, instance: PostRating, **kwargs): def post_on_create(sender, instance: Post, created, **kwargs): if created: if instance.image: - crop_model_image.delay(instance.pk, "blog", "Post") + crop_model_image.apply_async( + kwargs={ + "pk": instance.pk, + "app_label": "blog", + "model_name": "Post", + }, + countdown=10, + ) diff --git a/akarpov/templates/account/signup.html b/akarpov/templates/account/signup.html index 189ab9e..7b0a32d 100644 --- a/akarpov/templates/account/signup.html +++ b/akarpov/templates/account/signup.html @@ -2,6 +2,7 @@ {% load i18n %} {% load crispy_forms_tags %} +{% load socialaccount %} {% block head_title %}{% translate "Signup" %}{% endblock %} @@ -16,6 +17,9 @@ {% if redirect_field_value %} {% endif %} +
+ +
diff --git a/akarpov/templates/blog/post.html b/akarpov/templates/blog/post.html index 6c6962c..dff8a1a 100644 --- a/akarpov/templates/blog/post.html +++ b/akarpov/templates/blog/post.html @@ -74,7 +74,7 @@ {% csrf_token %}
- +
@@ -95,7 +95,7 @@

{{ comment.author.username }} - {{ comment.created | naturaltime }}

- +

{{ comment.body }} diff --git a/akarpov/templates/socialaccount/login.html b/akarpov/templates/socialaccount/login.html new file mode 100644 index 0000000..9fc1a79 --- /dev/null +++ b/akarpov/templates/socialaccount/login.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block head_title %}{% trans "Sign In" %}{% endblock %} + +{% block content %} +{% if process == "connect" %} +

{% blocktrans with provider.name as provider %}Connect {{ provider }}{% endblocktrans %}

+ +

{% blocktrans with provider.name as provider %}You are about to connect a new third party account from {{ provider }}.{% endblocktrans %}

+{% else %} +

{% blocktrans with provider.name as provider %}Sign In Via {{ provider }}{% endblocktrans %}

+ +

{% blocktrans with provider.name as provider %}You are about to sign in using a third party account from {{ provider }}.{% endblocktrans %}

+{% endif %} + +
+ {% csrf_token %} + +
+{% endblock %} diff --git a/akarpov/templates/socialaccount/providers/connections.html b/akarpov/templates/socialaccount/providers/connections.html new file mode 100644 index 0000000..b0eafa3 --- /dev/null +++ b/akarpov/templates/socialaccount/providers/connections.html @@ -0,0 +1,52 @@ +{% load i18n %} + +{% block head_title %}{% trans "Account Connections" %}{% endblock %} + +{% block content %} +

{% trans "Account Connections" %}

+ +{% if form.accounts %} +

{% blocktrans %}You can sign in to your account using any of the following third party accounts:{% endblocktrans %}

+ + +
+{% csrf_token %} + +
+{% if form.non_field_errors %} +
{{ form.non_field_errors }}
+{% endif %} + +{% for base_account in form.accounts %} +{% with base_account.get_provider_account as account %} +
+ +
+{% endwith %} +{% endfor %} + +
+ +
+ +
+ +
+ +{% else %} +

{% trans 'You currently have no social network accounts connected to this account.' %}

+{% endif %} + +

{% trans 'Add a 3rd Party Account' %}

+ + + +{% include "socialaccount/snippets/login_extra.html" %} + +{% endblock %} diff --git a/akarpov/templates/socialaccount/snippets/provider_list.html b/akarpov/templates/socialaccount/snippets/provider_list.html new file mode 100644 index 0000000..54e1d7f --- /dev/null +++ b/akarpov/templates/socialaccount/snippets/provider_list.html @@ -0,0 +1,21 @@ +{% load socialaccount %} + +{% get_providers as socialaccount_providers %} + +{% for provider in socialaccount_providers %} +{% if provider.id == "openid" %} +{% for brand in provider.get_brands %} +
  • + {{brand.name}} +
  • +{% endfor %} +{% endif %} +
  • + + {{provider.name}} +
  • +{% endfor %} diff --git a/akarpov/templates/users/user_detail.html b/akarpov/templates/users/user_detail.html index 156f7bb..e7f5d5e 100644 --- a/akarpov/templates/users/user_detail.html +++ b/akarpov/templates/users/user_detail.html @@ -35,10 +35,20 @@
  • - +
  • +
  • + + + +
  • +
  • + + + +
  • {% endif %} diff --git a/akarpov/users/signals.py b/akarpov/users/signals.py index 5febbc7..7b057cb 100644 --- a/akarpov/users/signals.py +++ b/akarpov/users/signals.py @@ -15,7 +15,14 @@ def on_change(sender, instance: User, **kwargs): {"image_cropped"} ): if instance.image: - crop_model_image.delay(instance.pk, "users", "User") + crop_model_image.apply_async( + kwargs={ + "pk": instance.pk, + "app_label": "users", + "model_name": "User", + }, + countdown=10, + ) else: instance.image_cropped = None instance.save() @@ -25,4 +32,12 @@ def on_change(sender, instance: User, **kwargs): def post_on_create(sender, instance: User, created, **kwargs): if created: if instance.image: - crop_model_image.delay(instance.pk, "users", "User") + if instance.image: + crop_model_image.apply_async( + kwargs={ + "pk": instance.pk, + "app_label": "users", + "model_name": "User", + }, + countdown=10, + ) diff --git a/config/settings/base.py b/config/settings/base.py index 10276bc..1dd96eb 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -66,6 +66,7 @@ "django.contrib.admin", "django.forms", ] + THIRD_PARTY_APPS = [ "crispy_forms", "crispy_bootstrap5", @@ -82,13 +83,34 @@ "colorfield", ] +HEALTH_CHECKS = [ + "health_check", # required + "health_check.db", # stock Django health checkers + "health_check.cache", + "health_check.storage", + "health_check.contrib.celery", + "health_check.contrib.celery_ping", + "health_check.contrib.migrations", + "health_check.contrib.psutil", # disk and memory utilization + "health_check.contrib.redis", +] + +ALL_AUTH_PROVIDERS = [ + "allauth.socialaccount.providers.github", + # "allauth.socialaccount.providers.google", + # "allauth.socialaccount.providers.telegram", + # "allauth.socialaccount.providers.yandex", +] + LOCAL_APPS = [ "akarpov.users", "akarpov.blog", # Your stuff: custom apps go here ] # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps -INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS +INSTALLED_APPS = ( + DJANGO_APPS + THIRD_PARTY_APPS + HEALTH_CHECKS + ALL_AUTH_PROVIDERS + LOCAL_APPS +) # MIGRATIONS # ------------------------------------------------------------------------------ @@ -284,6 +306,7 @@ CELERY_TASK_SOFT_TIME_LIMIT = 60 # https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-scheduler CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" + # django-allauth # ------------------------------------------------------------------------------ ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True) @@ -291,6 +314,9 @@ ACCOUNT_AUTHENTICATION_METHOD = "username" # https://django-allauth.readthedocs.io/en/latest/configuration.html ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True +ACCOUNT_LOGIN_ON_PASSWORD_RESET = True +SOCIALACCOUNT_EMAIL_VERIFICATION = False # https://django-allauth.readthedocs.io/en/latest/configuration.html ACCOUNT_EMAIL_VERIFICATION = "mandatory" # https://django-allauth.readthedocs.io/en/latest/configuration.html @@ -301,6 +327,23 @@ SOCIALACCOUNT_ADAPTER = "akarpov.users.adapters.SocialAccountAdapter" # https://django-allauth.readthedocs.io/en/latest/forms.html SOCIALACCOUNT_FORMS = {"signup": "akarpov.users.forms.UserSocialSignupForm"} +SOCIALACCOUNT_PROVIDERS = { + "github": { + "SCOPE": [ + "user", + "read:org", + ], + }, + "google": { + "SCOPE": [ + "profile", + "email", + ], + "AUTH_PARAMS": { + "access_type": "online", + }, + }, +} # django-rest-framework # ------------------------------------------------------------------------------- diff --git a/config/urls.py b/config/urls.py index 66f5a63..25fe306 100644 --- a/config/urls.py +++ b/config/urls.py @@ -15,6 +15,7 @@ path( "about/", TemplateView.as_view(template_name="pages/about.html"), name="about" ), + path("health/", include("health_check.urls")), # Django Admin, use {% url 'admin:index' %} path(settings.ADMIN_URL, admin.site.urls), # User management diff --git a/requirements/base.txt b/requirements/base.txt index 09fd1e9..264964b 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,4 +1,5 @@ pytz==2022.6 # https://github.com/stub42/pytz +psutil==5.9.4 python-slugify==6.1.2 # https://github.com/un33k/python-slugify Pillow==9.3.0 # https://github.com/python-pillow/Pillow argon2-cffi==21.3.0 # https://github.com/hynek/argon2_cffi @@ -12,6 +13,7 @@ flower==1.2.0 # https://github.com/mher/flower # Django # ------------------------------------------------------------------------------ django==4.0.8 # pyup: < 4.1 # https://www.djangoproject.com/ +django-health-check==3.17.0 django-environ==0.9.0 # https://github.com/joke2k/django-environ django-model-utils==4.2.0 # https://github.com/jazzband/django-model-utils django-allauth==0.51.0 # https://github.com/pennersr/django-allauth