From fd4590f6119babcef48f793e075802646e8cd896 Mon Sep 17 00:00:00 2001 From: Sayyid Hamid Mahdavi Date: Sat, 6 Sep 2025 12:22:07 +0330 Subject: [PATCH] email --- apps/emails/admin.py | 4 --- apps/emails/migrations/0001_initial.py | 37 +++++++++++++++++++ apps/emails/models.py | 22 +++++++++--- apps/emails/serializers.py | 2 ++ apps/emails/tasks.py | 6 ++-- apps/emails/urls.py | 50 ++------------------------ apps/emails/views.py | 2 +- apps/users/admin.py | 5 ++- main/settings.py | 13 +++++++ main/urls.py | 1 + 10 files changed, 81 insertions(+), 61 deletions(-) create mode 100644 apps/emails/migrations/0001_initial.py diff --git a/apps/emails/admin.py b/apps/emails/admin.py index 34ac502..15397f5 100644 --- a/apps/emails/admin.py +++ b/apps/emails/admin.py @@ -3,9 +3,5 @@ from django.contrib import admin from .models import Email - - - - admin.site.register(Email) diff --git a/apps/emails/migrations/0001_initial.py b/apps/emails/migrations/0001_initial.py new file mode 100644 index 0000000..5fa1bc9 --- /dev/null +++ b/apps/emails/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 5.1.4 on 2025-09-06 08:28 + +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + migrations.swappable_dependency(settings.OAUTH2_PROVIDER_APPLICATION_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Email', + fields=[ + ('uuid', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), + ('updated_at', models.DateTimeField(auto_now=True, db_index=True)), + ('title', models.CharField(max_length=128)), + ('message', models.TextField()), + ('priority', models.IntegerField(default=5)), + ('extras', models.JSONField(default=dict)), + ('stats', models.IntegerField(choices=[(0, 'init'), (1, 'Start'), (2, 'done'), (3, 'timeout'), (4, 'failed')], default=0)), + ('application', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/emails/models.py b/apps/emails/models.py index 19dc24c..33e0602 100644 --- a/apps/emails/models.py +++ b/apps/emails/models.py @@ -18,20 +18,34 @@ class Email(BaseModel): TIMEOUT = 3, _('timeout') FAILED = 4, _('failed') - user = models.ForeignKey(User, on_delete=models.PROTECT) + user = models.ForeignKey(User, on_delete=models.PROTECT, related_name='+') application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT, related_name='+', null=True, blank=True) title = models.CharField(max_length=128) message = models.TextField() priority = models.IntegerField(default=5) - extras = models.JSONField(default=dict) + extras = models.JSONField(default=dict, null=True, blank=True) stats = models.IntegerField(default=StateChoices.INIT, choices=StateChoices.choices) # bulk = models.ForeignKey("BulkEmail", null=True, blank=True, on_delete=models.PROTECT) objects = EmailQuerySet.as_manager() - def send_email(self): + def __str__(self): + return self.title + + def send(self): from apps.emails.tasks import send_email - send_email.delay(str(self.uuid)) + send_email(self.uuid) + + def _send_email(self): + from django.core.mail import send_mail + from django.conf import settings + send_mail( + self.title, + self.message, + settings.EMAIL_HOST_USER, + ["xdshia49@gmail.com"], + fail_silently=False, + ) diff --git a/apps/emails/serializers.py b/apps/emails/serializers.py index 779935c..e4d58a3 100644 --- a/apps/emails/serializers.py +++ b/apps/emails/serializers.py @@ -7,6 +7,8 @@ class EmailSerializer(serializers.ModelSerializer): class Meta: model = Email fields = ( + "title", + "message", "user", "application" ) diff --git a/apps/emails/tasks.py b/apps/emails/tasks.py index 3ef3fe2..8ba11b5 100644 --- a/apps/emails/tasks.py +++ b/apps/emails/tasks.py @@ -3,10 +3,10 @@ from utils.clients import gotify @celery_app.task -def send_email(push_message_uuid): +def send_email(email_uuid): from .models import Email - email = Email.objects.get(uuid=push_message_uuid) - email.send_email() + email = Email.objects.get(uuid=email_uuid) + email._send_email() email.state = email.StateChoices.DONE email.save() diff --git a/apps/emails/urls.py b/apps/emails/urls.py index 8293d59..81211a6 100644 --- a/apps/emails/urls.py +++ b/apps/emails/urls.py @@ -4,56 +4,10 @@ from . import views from django.urls import NoReverseMatch, path, re_path -app_name = 'push_notifications' - - -class ApplicationRouter(DefaultRouter): - - routes = [ - # List route. - Route( - url=r'^{prefix}/{trailing_slash}$', - mapping={ - 'get': 'list', - 'post': 'create' - }, - name='{basename}-list', - detail=False, - initkwargs={'suffix': 'List'} - ), - # Dynamically generated list routes. Generated using - # @action(detail=False) decorator on methods of the viewset. - DynamicRoute( - url=r'^{prefix}//{url_path}{trailing_slash}$', - name='{basename}-{url_name}', - detail=False, - initkwargs={} - ), - # # Detail route. - Route( - url=r'^{prefix}//{lookup}{trailing_slash}$', - mapping={ - 'get': 'retrieve', - 'put': 'update', - 'patch': 'partial_update', - 'delete': 'destroy' - }, - name='{basename}-detail', - detail=True, - initkwargs={'suffix': 'Instance'} - ), - # # Dynamically generated detail routes. Generated using - # # @action(detail=True) decorator on methods of the viewset. - DynamicRoute( - url=r'^{prefix}//{lookup}/{url_path}{trailing_slash}$', - name='{basename}-{url_name}', - detail=True, - initkwargs={} - ), - ] +app_name = 'emails' application_router = DefaultRouter() -application_router.register('application', views.ApplicationPushUserViewSet, basename='application') +application_router.register('application', views.ApplicationEmailViewSet, basename='application') urlpatterns = application_router.urls diff --git a/apps/emails/views.py b/apps/emails/views.py index ed336ca..f23393c 100644 --- a/apps/emails/views.py +++ b/apps/emails/views.py @@ -43,4 +43,4 @@ class ApplicationEmailViewSet( user = get_object_or_404(User, uuid=user_uuid) application = get_application(self.request) instance = serializer.save(user=user, application=application) - instance.send_email() + instance.send() diff --git a/apps/users/admin.py b/apps/users/admin.py index 41a4e09..e3366e4 100644 --- a/apps/users/admin.py +++ b/apps/users/admin.py @@ -1,5 +1,8 @@ from django.contrib import admin from .models import User +class UserAdmin(admin.ModelAdmin): + list_display = ('uuid', 'username', 'first_name', 'last_name') + search_fields = ('uuid', 'username', 'first_name', 'last_name') -admin.site.register(User) +admin.site.register(User, UserAdmin) diff --git a/main/settings.py b/main/settings.py index 14c3113..0ed3d61 100644 --- a/main/settings.py +++ b/main/settings.py @@ -55,6 +55,7 @@ INSTALLED_APPS = [ 'apps.users', 'apps.gooyal_oauth2', 'apps.push_notifications', + 'apps.emails' ] MIDDLEWARE = [ @@ -340,3 +341,15 @@ CELERY_RESULT_BACKEND = REDIS_BASE_URL # status_forcelist=[500, 502, 503, 504] # ) # ) + +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" +EMAIL_HOST = config('EMAIL_HOST') +EMAIL_PORT = config('EMAIL_PORT', cast=int) +EMAIL_HOST_USER = config('EMAIL_HOST_USER') +EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') +EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=False, cast=bool) +EMAIL_USE_SSL = config('EMAIL_USE_SSL', default=False, cast=bool) +EMAIL_TIMEOUT = config('EMAIL_TIMEOUT', cast=int) +EMAIL_SSL_KEYFILE = config('EMAIL_SSL_KEYFILE', None) +EMAIL_SSL_CERTFILE = config('EMAIL_SSL_CERTFILE', None) + diff --git a/main/urls.py b/main/urls.py index 1605331..a0b361c 100644 --- a/main/urls.py +++ b/main/urls.py @@ -37,6 +37,7 @@ urlpatterns = [ path('', include('apps.core.urls')), path('push/user/', include('apps.push_notifications.user_urls')), path('push/application//', include('apps.push_notifications.application_urls')), + path('email/application//', include('apps.emails.urls')), # path('users/', include('apps.users.urls')), # path('oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')),