From 7af1af9e5c5c6ed55d97c65f9b1b92dd88392c68 Mon Sep 17 00:00:00 2001 From: Sayyid Hamid Mahdavi Date: Tue, 1 Jul 2025 12:35:51 +0330 Subject: [PATCH] get client token --- apps/notifications/admin.py | 3 +- apps/notifications/apps.py | 2 +- apps/notifications/migrations/0001_initial.py | 32 +++ apps/notifications/models.py | 30 ++- apps/notifications/serializers.py | 200 +----------------- apps/notifications/urls.py | 57 +++++ apps/notifications/views.py | 37 +++- main/settings.py | 5 +- main/urls.py | 3 +- utils/clients/gotify.py | 23 +- 10 files changed, 170 insertions(+), 222 deletions(-) create mode 100644 apps/notifications/migrations/0001_initial.py create mode 100644 apps/notifications/urls.py diff --git a/apps/notifications/admin.py b/apps/notifications/admin.py index 8932ae8..488f9e4 100644 --- a/apps/notifications/admin.py +++ b/apps/notifications/admin.py @@ -1,6 +1,5 @@ from django.contrib import admin -from .models import PushUser, Device +from .models import PushUser admin.site.register(PushUser) -admin.site.register(Device) \ No newline at end of file diff --git a/apps/notifications/apps.py b/apps/notifications/apps.py index 001b4f9..03a81ff 100644 --- a/apps/notifications/apps.py +++ b/apps/notifications/apps.py @@ -3,4 +3,4 @@ from django.apps import AppConfig class NotificationsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' - name = 'notifications' + name = 'apps.notifications' diff --git a/apps/notifications/migrations/0001_initial.py b/apps/notifications/migrations/0001_initial.py new file mode 100644 index 0000000..a914bc0 --- /dev/null +++ b/apps/notifications/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 5.2.3 on 2025-07-01 08:37 + +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), + ] + + operations = [ + migrations.CreateModel( + name='PushUser', + 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)), + ('client_token', models.CharField(blank=True, max_length=32, null=True)), + ('application_token', models.CharField(blank=True, max_length=32, null=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/notifications/models.py b/apps/notifications/models.py index 7271692..8ac5930 100644 --- a/apps/notifications/models.py +++ b/apps/notifications/models.py @@ -2,18 +2,40 @@ from django.db import models from apps.users.models import User from utils.models import BaseModel +from utils.clients import gotify +class PushUserQuerySet(models.QuerySet): + def submit(self, user): + push_user, created = self.get_or_create(user=user) + push_user: PushUser = push_user + push_user.submit_user() + push_user.submit_client() + push_user.submit_application() + return push_user + class PushUser(BaseModel): user = models.ForeignKey(User, on_delete=models.CASCADE) client_token = models.CharField(max_length=32, null=True, blank=True) application_token = models.CharField(max_length=32, null=True, blank=True) - def get_token(self): - return + objects = PushUserQuerySet.as_manager() + + def submit_user(self): + response = gotify.submit_user(str(self.user.uuid)) + + def submit_client(self): + if not self.client_token: + response = gotify.submit_client(str(self.user.uuid)) + self.client_token = response.token + self.save() + + def submit_application(self): + if not self.application_token: + response = gotify.submit_application(self.client_token, str(self.user.uuid)) + self.application_token = response.token + self.save() def send_push(self): return -class Device(BaseModel): - pass diff --git a/apps/notifications/serializers.py b/apps/notifications/serializers.py index 7c18961..391c7a4 100644 --- a/apps/notifications/serializers.py +++ b/apps/notifications/serializers.py @@ -1,202 +1,12 @@ from rest_framework import serializers -from .models import PushDevice - -from ..core.models import Config +from .models import PushUser -class CategorySerializer(serializers.ModelSerializer): +class PushSerializer(serializers.ModelSerializer): class Meta: - model = Category + model = PushUser fields = ( - 'uuid', - 'color', - 'title', - 'icon', + "user", + "client_token" ) - - -class VisitSerializer(serializers.ModelSerializer): - class Meta: - model = Visit - fields = ( - 'uuid', - 'ad', - 'state', - 'reward_amount', - 'location', - 'opportunity_until', - 'display_time', - ) - read_only_fields = ('uuid', 'state', 'reward_amount', 'opportunity_until', 'display_time') - - -class AdSerializer(TaggitSerializer, serializers.ModelSerializer): - tags = TagListSerializerField(read_only=True) - category_detail = CategorySerializer(read_only=True) - - class Meta: - model = Ad - fields = ( - 'uuid', - 'title', - 'content', - 'content_type', - 'description', - 'location', - 'category', - 'category_detail', - 'banner', - 'reward_amount', - 'reward_count', - 'address', - 'url', - 'tags', - 'created_at', - 'updated_at', - 'display_time', - 'status', - ) - read_only_fields = fields - - -class AdWithVisitSerializer(TaggitSerializer, serializers.ModelSerializer): - visit = VisitSerializer(read_only=True) - - tags = TagListSerializerField(read_only=True) - category_detail = CategorySerializer(read_only=True) - - class Meta: - model = Ad - fields = ( - 'uuid', - 'title', - 'content', - 'content_type', - 'description', - 'location', - 'category', - 'category_detail', - 'banner', - 'reward_amount', - 'reward_count', - 'address', - 'url', - 'tags', - 'created_at', - 'updated_at', - 'display_time', - 'visit' - ) - read_only_fields = fields - -class AdUserSerializer(TaggitSerializer, serializers.ModelSerializer): - tags = TagListSerializerField(allow_null=True, allow_empty=True, required=False, read_only=True) - category_detail = CategorySerializer(read_only=True) - - # new_tags = TagListSerializerField(allow_null=True, allow_empty=True) - new_category_detail = CategorySerializer(read_only=True) - - class Meta: - model = Ad - fields = ( - 'uuid', - 'title', - 'content', - 'content_type', - 'description', - 'location', - 'category', - 'category_detail', - 'banner', - 'reward_amount', - 'reward_count', - 'address', - 'url', - 'tags', - - 'new_title', - 'new_location', - 'new_category', - 'new_category_detail', - 'new_description', - 'new_banner', - 'new_content', - 'new_content_type', - 'new_url', - 'new_tags', - 'new_address', - - 'payment_state', - 'state', - 'rejection_reason', - 'created_at', - 'updated_at', - 'display_time', - 'status', - ) - read_only_fields = ['uuid', - 'title', - 'content', - 'content_type', - 'description', - 'location', - 'category', - 'category_detail', - 'banner', - 'address', - 'url', - 'tags', - - 'new_category_detail', - - 'payment_state', - 'state', - 'rejection_reason', - 'created_at', - 'updated_at', - 'display_time', - 'status' - ] - - -class TagSerializer(TaggitSerializer, serializers.ModelSerializer): - class Meta: - model = Tag - fields = ('name', 'slug') - - -class ConfigSerializer(serializers.ModelSerializer): - class Meta: - model = Config - fields = ['key', 'value', 'value_type'] - - - -# دریافتی -# کل بیلبورد -# کل بازدید -# درآمد سالیانه - - -class AdVisitStatisticSerializer(serializers.Serializer): - label = serializers.CharField() - with_reward_count = serializers.IntegerField() - without_reward_count = serializers.IntegerField() - reward_amount = serializers.IntegerField() - - -class StatisticSerializer(serializers.Serializer): - total_reward_input = serializers.IntegerField() - total_visit_count = serializers.IntegerField() - total_ads_count = serializers.IntegerField() - - monthly_data = AdVisitStatisticSerializer(many=True) - daily_data = AdVisitStatisticSerializer(many=True) - - -class ClusterSerializer(serializers.Serializer): - cluster = serializers.IntegerField() - ads_rewards_amount = serializers.IntegerField() - ads_count = serializers.IntegerField() - location = GeometryField() diff --git a/apps/notifications/urls.py b/apps/notifications/urls.py new file mode 100644 index 0000000..8cbac75 --- /dev/null +++ b/apps/notifications/urls.py @@ -0,0 +1,57 @@ +from rest_framework import routers +from rest_framework.routers import DefaultRouter, DynamicRoute, Route +from . import views + + +class CRUDRouter(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}{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}/{url_path}{trailing_slash}$', + name='{basename}-{url_name}', + detail=True, + initkwargs={} + ), + ] + + +router = CRUDRouter() + +app_name = 'notifications' + +router.register('push_user', views.PushUserViewSet, basename='user-data') + +urlpatterns = router.urls diff --git a/apps/notifications/views.py b/apps/notifications/views.py index 91ea44a..8948357 100644 --- a/apps/notifications/views.py +++ b/apps/notifications/views.py @@ -1,3 +1,38 @@ from django.shortcuts import render -# Create your views here. +from django.http import Http404 +from django.shortcuts import render +from oauth2_provider.contrib.rest_framework import TokenMatchesOASRequirements +from rest_framework import viewsets, mixins +from rest_framework.generics import get_object_or_404 +from rest_framework.viewsets import GenericViewSet + +from .models import PushUser +from .serializers import PushSerializer +from apps.gooyal_oauth2.models import Application +from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements +from apps.gooyal_oauth2.utils import get_application + + +class PushUserViewSet(mixins.RetrieveModelMixin, + # mixins.UpdateModelMixin, + # mixins.DestroyModelMixin, + GenericViewSet): + queryset = PushUser.objects.all() + serializer_class = PushSerializer + + permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements] + required_alternate_scopes = { + "GET": [["notifications.push:retrieve"]], + } + + def get_object(self): + user = self.request.user + auth = self.request.auth + if user: + # plan = Plan.objects.filter(application_id=auth.application_id).first() + push_user = PushUser.objects.submit(user) + return push_user + + raise Http404 + diff --git a/main/settings.py b/main/settings.py index b06ac71..473e16c 100644 --- a/main/settings.py +++ b/main/settings.py @@ -54,7 +54,7 @@ INSTALLED_APPS = [ 'apps.core', 'apps.users', 'apps.gooyal_oauth2', - # 'apps.stores', + 'apps.notifications', ] MIDDLEWARE = [ @@ -138,14 +138,13 @@ WSGI_APPLICATION = 'main.wsgi.application' DATABASES = { 'default': { - "ENGINE": "django.contrib.gis.db.backends.postgis", + "ENGINE": "django.db.backends.postgresql", 'NAME': config('DB_NAME'), 'USER': config('DB_USER'), 'PASSWORD': config('DB_PASSWORD'), 'HOST': config('DB_HOST', '127.0.0.1'), 'PORT': config('DB_PORT', ''), } - } # Password validation diff --git a/main/urls.py b/main/urls.py index 771e8c7..56f5f0e 100644 --- a/main/urls.py +++ b/main/urls.py @@ -35,8 +35,9 @@ urlpatterns = [ path('admin/', admin.site.urls), path('', include('apps.core.urls')), + path('', include('apps.notifications.urls')), # path('users/', include('apps.users.urls')), - path('oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')), + # path('oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')), ] diff --git a/utils/clients/gotify.py b/utils/clients/gotify.py index 642ca0b..52ae454 100644 --- a/utils/clients/gotify.py +++ b/utils/clients/gotify.py @@ -87,7 +87,7 @@ def submit_user(user_uuid): final_data = Tmp() client = get_client(ADMIN_CLIENT_TOKEN) - response: Any = create_user.sync(client=client, body=final_data) + response: UserExternalModel = create_user.sync(client=client, body=final_data) return response @@ -103,25 +103,18 @@ def submit_client(user_uuid): final_data = Tmp() client = get_basic_client(user_uuid, user_uuid) - response: Any = create_client.sync(client=client, body=final_data) + response: ClientModel = create_client.sync(client=client, body=final_data) return response def submit_application(token, user_uuid): - data_dict = { - "defaultPriority": 5, - "description": None, - "name": user_uuid - } - - class Tmp(): - def to_dict(self): - return data_dict - - final_data = Tmp() + final_data = ApplicationParamsModel( + default_priority=5, + name=user_uuid + ) client = get_client(token) - response: Any = create_app.sync(client=client, body=final_data) + response: ApplicationModel = create_app.sync(client=client, body=final_data) return response @@ -137,4 +130,4 @@ sample_data = { # send_notif(sample_data) # print(submit_user('820c1110-a3bd-47ce-a3f4-f8d2dd70fa9f')) # print(submit_client('820c1110-a3bd-47ce-a3f4-f8d2dd70fa9f')) -print(submit_application('CbNecn03yWNIl93','820c1110-a3bd-47ce-a3f4-f8d2dd70fa9f')) +# print(submit_application('CbNecn03yWNIl93', '820c1110-a3bd-47ce-a3f4-f8d2dd70fa9f'))