added oauth, health checks, new blog and user features

This commit is contained in:
Alexander Karpov 2022-11-25 12:16:10 +03:00
parent bf8e191a65
commit 9b2cf1656b
13 changed files with 209 additions and 9 deletions

View File

@ -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']},
),
]

View File

@ -98,7 +98,7 @@ def __str__(self):
return f"{self.author.username}'s comment on {self.post.title}" return f"{self.author.username}'s comment on {self.post.title}"
class Meta: class Meta:
ordering = ["-rating"] ordering = ["-rating", "-created"]
class CommentRating(models.Model): class CommentRating(models.Model):

View File

@ -19,7 +19,14 @@ def post_on_save(sender, instance: Post, **kwargs):
{"image_cropped"} {"image_cropped"}
): ):
if instance.image: 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: else:
instance.image_cropped = None instance.image_cropped = None
instance.save() instance.save()
@ -74,4 +81,11 @@ def post_rating_delete(sender, instance: PostRating, **kwargs):
def post_on_create(sender, instance: Post, created, **kwargs): def post_on_create(sender, instance: Post, created, **kwargs):
if created: if created:
if instance.image: 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,
)

View File

@ -2,6 +2,7 @@
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% load socialaccount %}
{% block head_title %}{% translate "Signup" %}{% endblock %} {% block head_title %}{% translate "Signup" %}{% endblock %}
@ -16,6 +17,9 @@
{% if redirect_field_value %} {% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" /> <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %} {% endif %}
<div class="row mb-2">
<a href="{% provider_login_url 'github' %}"><i class="bi bi-github btn"></i></a>
</div>
<button class="btn btn-primary" type="submit">{% translate "Sign Up" %} &raquo;</button> <button class="btn btn-primary" type="submit">{% translate "Sign Up" %} &raquo;</button>
</form> </form>

View File

@ -74,7 +74,7 @@
{% csrf_token %} {% csrf_token %}
<div> <div>
<label for="textarea" class="form-label">write your comments</label> <label for="textarea" class="form-label">write your comments</label>
<textarea name="body" class="form-control" id="textarea" rows="3"></textarea> <textarea name="body" maxlength="100" class="form-control" id="textarea" rows="3"></textarea>
</div> </div>
<button class="btn btn-sm btn-secondary mt-2" type="submit">Send</button> <button class="btn btn-sm btn-secondary mt-2" type="submit">Send</button>
</form> </form>
@ -95,7 +95,7 @@
<p class="mb-1"> <p class="mb-1">
{{ comment.author.username }} <span class="small">- {{ comment.created | naturaltime }}</span> {{ comment.author.username }} <span class="small">- {{ comment.created | naturaltime }}</span>
</p> </p>
<button class="btn btn-outline-primary"><i class="bi bi-reply"></i><span class="small"> reply</span></button> <!--<button class="btn btn-outline-primary"><i class="bi bi-reply"></i><span class="small"> reply</span></button> -->
</div> </div>
<p class="small mb-0"> <p class="small mb-0">
{{ comment.body }} {{ comment.body }}

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Sign In" %}{% endblock %}
{% block content %}
{% if process == "connect" %}
<h1>{% blocktrans with provider.name as provider %}Connect {{ provider }}{% endblocktrans %}</h1>
<p>{% blocktrans with provider.name as provider %}You are about to connect a new third party account from {{ provider }}.{% endblocktrans %}</p>
{% else %}
<h1>{% blocktrans with provider.name as provider %}Sign In Via {{ provider }}{% endblocktrans %}</h1>
<p>{% blocktrans with provider.name as provider %}You are about to sign in using a third party account from {{ provider }}.{% endblocktrans %}</p>
{% endif %}
<form method="post">
{% csrf_token %}
<button class="btn btn-success" type="submit">{% trans "Continue" %}</button>
</form>
{% endblock %}

View File

@ -0,0 +1,52 @@
{% load i18n %}
{% block head_title %}{% trans "Account Connections" %}{% endblock %}
{% block content %}
<h1>{% trans "Account Connections" %}</h1>
{% if form.accounts %}
<p>{% blocktrans %}You can sign in to your account using any of the following third party accounts:{% endblocktrans %}</p>
<form method="post" action="{% url 'socialaccount_connections' %}">
{% csrf_token %}
<fieldset>
{% if form.non_field_errors %}
<div id="errorMsg">{{ form.non_field_errors }}</div>
{% endif %}
{% for base_account in form.accounts %}
{% with base_account.get_provider_account as account %}
<div>
<label for="id_account_{{ base_account.id }}">
<input id="id_account_{{ base_account.id }}" type="radio" name="account" value="{{ base_account.id }}"/>
<span class="socialaccount_provider {{ base_account.provider }} {{ account.get_brand.id }}">{{account.get_brand.name}}</span>
{{ account }}
</label>
</div>
{% endwith %}
{% endfor %}
<div>
<button type="submit">{% trans 'Remove' %}</button>
</div>
</fieldset>
</form>
{% else %}
<p>{% trans 'You currently have no social network accounts connected to this account.' %}</p>
{% endif %}
<h2>{% trans 'Add a 3rd Party Account' %}</h2>
<ul class="socialaccount_providers">
{% include "socialaccount/snippets/provider_list.html" with process="connect" %}
</ul>
{% include "socialaccount/snippets/login_extra.html" %}
{% endblock %}

View File

@ -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 %}
<li>
<a title="{{brand.name | lower}}"
class="socialaccount_provider {{provider.id}} {{brand.id}}"
href="{% provider_login_url provider.id openid=brand.openid_url process=process %}"
><i class="bi bi-{{ brand.name }}"></i>{{brand.name}}</a>
</li>
{% endfor %}
{% endif %}
<li>
<i class="bi bi-{{provider.name | lower}}"></i>
<a title="{{provider.name}}" class="socialaccount_provider {{provider.id}}"
href="{% provider_login_url provider.id process=process scope=scope auth_params=auth_params %}">{{provider.name}}</a>
</li>
{% endfor %}

View File

@ -35,10 +35,20 @@
</a> </a>
</li> </li>
<li> <li>
<a href="{% url 'users:update' %}"> <a href="{% url 'account_email' %}">
<i class="bi bi-envelope"></i> <i class="bi bi-envelope"></i>
</a> </a>
</li> </li>
<li>
<a href="{% url 'account_reset_password' %}">
<i class="bi bi-key"></i>
</a>
</li>
<li>
<a href="{% url 'socialaccount_connections' %}">
<i class="bi bi-person-add"></i>
</a>
</li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -15,7 +15,14 @@ def on_change(sender, instance: User, **kwargs):
{"image_cropped"} {"image_cropped"}
): ):
if instance.image: 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: else:
instance.image_cropped = None instance.image_cropped = None
instance.save() instance.save()
@ -25,4 +32,12 @@ def on_change(sender, instance: User, **kwargs):
def post_on_create(sender, instance: User, created, **kwargs): def post_on_create(sender, instance: User, created, **kwargs):
if created: if created:
if instance.image: 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,
)

View File

@ -66,6 +66,7 @@
"django.contrib.admin", "django.contrib.admin",
"django.forms", "django.forms",
] ]
THIRD_PARTY_APPS = [ THIRD_PARTY_APPS = [
"crispy_forms", "crispy_forms",
"crispy_bootstrap5", "crispy_bootstrap5",
@ -82,13 +83,34 @@
"colorfield", "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 = [ LOCAL_APPS = [
"akarpov.users", "akarpov.users",
"akarpov.blog", "akarpov.blog",
# Your stuff: custom apps go here # Your stuff: custom apps go here
] ]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps # 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 # MIGRATIONS
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -284,6 +306,7 @@
CELERY_TASK_SOFT_TIME_LIMIT = 60 CELERY_TASK_SOFT_TIME_LIMIT = 60
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-scheduler # https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-scheduler
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
# django-allauth # django-allauth
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True) ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)
@ -291,6 +314,9 @@
ACCOUNT_AUTHENTICATION_METHOD = "username" ACCOUNT_AUTHENTICATION_METHOD = "username"
# https://django-allauth.readthedocs.io/en/latest/configuration.html # https://django-allauth.readthedocs.io/en/latest/configuration.html
ACCOUNT_EMAIL_REQUIRED = True 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 # https://django-allauth.readthedocs.io/en/latest/configuration.html
ACCOUNT_EMAIL_VERIFICATION = "mandatory" ACCOUNT_EMAIL_VERIFICATION = "mandatory"
# https://django-allauth.readthedocs.io/en/latest/configuration.html # https://django-allauth.readthedocs.io/en/latest/configuration.html
@ -301,6 +327,23 @@
SOCIALACCOUNT_ADAPTER = "akarpov.users.adapters.SocialAccountAdapter" SOCIALACCOUNT_ADAPTER = "akarpov.users.adapters.SocialAccountAdapter"
# https://django-allauth.readthedocs.io/en/latest/forms.html # https://django-allauth.readthedocs.io/en/latest/forms.html
SOCIALACCOUNT_FORMS = {"signup": "akarpov.users.forms.UserSocialSignupForm"} 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 # django-rest-framework
# ------------------------------------------------------------------------------- # -------------------------------------------------------------------------------

View File

@ -15,6 +15,7 @@
path( path(
"about/", TemplateView.as_view(template_name="pages/about.html"), name="about" "about/", TemplateView.as_view(template_name="pages/about.html"), name="about"
), ),
path("health/", include("health_check.urls")),
# Django Admin, use {% url 'admin:index' %} # Django Admin, use {% url 'admin:index' %}
path(settings.ADMIN_URL, admin.site.urls), path(settings.ADMIN_URL, admin.site.urls),
# User management # User management

View File

@ -1,4 +1,5 @@
pytz==2022.6 # https://github.com/stub42/pytz pytz==2022.6 # https://github.com/stub42/pytz
psutil==5.9.4
python-slugify==6.1.2 # https://github.com/un33k/python-slugify python-slugify==6.1.2 # https://github.com/un33k/python-slugify
Pillow==9.3.0 # https://github.com/python-pillow/Pillow Pillow==9.3.0 # https://github.com/python-pillow/Pillow
argon2-cffi==21.3.0 # https://github.com/hynek/argon2_cffi 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
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
django==4.0.8 # pyup: < 4.1 # https://www.djangoproject.com/ 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-environ==0.9.0 # https://github.com/joke2k/django-environ
django-model-utils==4.2.0 # https://github.com/jazzband/django-model-utils django-model-utils==4.2.0 # https://github.com/jazzband/django-model-utils
django-allauth==0.51.0 # https://github.com/pennersr/django-allauth django-allauth==0.51.0 # https://github.com/pennersr/django-allauth