send message api
This commit is contained in:
parent
da9e640440
commit
40d3579d13
17 changed files with 229 additions and 148 deletions
|
|
@ -1,5 +0,0 @@
|
||||||
from django.contrib import admin
|
|
||||||
from .models import PushUser
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(PushUser)
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
# 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,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
from rest_framework import serializers
|
|
||||||
|
|
||||||
from .models import PushUser
|
|
||||||
|
|
||||||
|
|
||||||
class PushSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = PushUser
|
|
||||||
fields = (
|
|
||||||
"user",
|
|
||||||
"client_token",
|
|
||||||
"application_token"
|
|
||||||
)
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
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:get_client_token"]],
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
6
apps/push_notifications/admin.py
Normal file
6
apps/push_notifications/admin.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from .models import PushUser, PushMessage
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(PushUser)
|
||||||
|
admin.site.register(PushMessage)
|
||||||
59
apps/push_notifications/application_urls.py
Normal file
59
apps/push_notifications/application_urls.py
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
from rest_framework import routers
|
||||||
|
from rest_framework.routers import DefaultRouter, DynamicRoute, Route
|
||||||
|
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}/<user_uuid>{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}/<user_uuid>/{url_path}{trailing_slash}$',
|
||||||
|
name='{basename}-{url_name}',
|
||||||
|
detail=False,
|
||||||
|
initkwargs={}
|
||||||
|
),
|
||||||
|
# # Detail route.
|
||||||
|
Route(
|
||||||
|
url=r'^{prefix}/<user_uuid>/{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}/<user_uuid>/{lookup}/{url_path}{trailing_slash}$',
|
||||||
|
name='{basename}-{url_name}',
|
||||||
|
detail=True,
|
||||||
|
initkwargs={}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
application_router = DefaultRouter()
|
||||||
|
application_router.register('application', views.ApplicationPushUserViewSet, basename='application')
|
||||||
|
|
||||||
|
urlpatterns = application_router.urls
|
||||||
|
|
@ -3,4 +3,4 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
class NotificationsConfig(AppConfig):
|
class NotificationsConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'apps.notifications'
|
name = 'apps.push_notifications'
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from oauth2_provider.settings import oauth2_settings
|
||||||
|
|
||||||
from apps.users.models import User
|
from apps.users.models import User
|
||||||
from utils.models import BaseModel
|
from utils.models import BaseModel
|
||||||
|
|
@ -15,9 +16,11 @@ class PushUserQuerySet(models.QuerySet):
|
||||||
return push_user
|
return push_user
|
||||||
|
|
||||||
class PushUser(BaseModel):
|
class PushUser(BaseModel):
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||||
client_token = models.CharField(max_length=32, null=True, blank=True)
|
client_token = models.CharField(max_length=32, null=True, blank=True)
|
||||||
application_token = models.CharField(max_length=32, null=True, blank=True)
|
application_token = models.CharField(max_length=32, null=True, blank=True)
|
||||||
|
app_id = None
|
||||||
|
|
||||||
|
|
||||||
objects = PushUserQuerySet.as_manager()
|
objects = PushUserQuerySet.as_manager()
|
||||||
|
|
||||||
|
|
@ -39,3 +42,17 @@ class PushUser(BaseModel):
|
||||||
def send_push(self):
|
def send_push(self):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class PushMessage(BaseModel):
|
||||||
|
push_user = models.ForeignKey(PushUser, on_delete=models.PROTECT)
|
||||||
|
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)
|
||||||
|
|
||||||
|
def send_push(self):
|
||||||
|
gotify.send_notif(token=self.push_user.application_token ,title=self.title, message=self.message, priority=self.priority, extras=self.extras)
|
||||||
|
|
||||||
|
|
||||||
26
apps/push_notifications/serializers.py
Normal file
26
apps/push_notifications/serializers.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import PushUser, PushMessage
|
||||||
|
|
||||||
|
|
||||||
|
class PushUserSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = PushUser
|
||||||
|
fields = (
|
||||||
|
"user",
|
||||||
|
"client_token",
|
||||||
|
"application_token"
|
||||||
|
)
|
||||||
|
|
||||||
|
class PushMessageSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = PushMessage
|
||||||
|
fields = (
|
||||||
|
"push_user",
|
||||||
|
"application",
|
||||||
|
"title",
|
||||||
|
"message",
|
||||||
|
"priority",
|
||||||
|
"extras"
|
||||||
|
)
|
||||||
|
read_only_fields = ("push_user","application")
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
from rest_framework.routers import DefaultRouter, DynamicRoute, Route
|
from rest_framework.routers import DefaultRouter, DynamicRoute, Route
|
||||||
from . import views
|
from . import views
|
||||||
|
from django.urls import NoReverseMatch, path, re_path
|
||||||
|
|
||||||
|
|
||||||
class CRUDRouter(DefaultRouter):
|
app_name = 'push_notifications'
|
||||||
|
|
||||||
|
class PushUserRouter(DefaultRouter):
|
||||||
routes = [
|
routes = [
|
||||||
# List route.
|
# List route.
|
||||||
Route(
|
Route(
|
||||||
|
|
@ -47,11 +50,8 @@ class CRUDRouter(DefaultRouter):
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
user_router = PushUserRouter()
|
||||||
|
user_router.register('push_user', views.PushUserViewSet, basename='user-data')
|
||||||
|
|
||||||
router = CRUDRouter()
|
|
||||||
|
|
||||||
app_name = 'notifications'
|
urlpatterns = user_router.urls
|
||||||
|
|
||||||
router.register('push_user', views.PushUserViewSet, basename='user-data')
|
|
||||||
|
|
||||||
urlpatterns = router.urls
|
|
||||||
69
apps/push_notifications/views.py
Normal file
69
apps/push_notifications/views.py
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
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, PushMessage
|
||||||
|
from .serializers import PushUserSerializer, PushMessageSerializer
|
||||||
|
from apps.gooyal_oauth2.models import Application
|
||||||
|
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
||||||
|
from apps.gooyal_oauth2.utils import get_application
|
||||||
|
from ..users.models import User
|
||||||
|
|
||||||
|
|
||||||
|
class PushUserViewSet(mixins.RetrieveModelMixin,
|
||||||
|
# mixins.UpdateModelMixin,
|
||||||
|
# mixins.DestroyModelMixin,
|
||||||
|
GenericViewSet):
|
||||||
|
queryset = PushUser.objects.all()
|
||||||
|
serializer_class = PushUserSerializer
|
||||||
|
|
||||||
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
||||||
|
required_alternate_scopes = {
|
||||||
|
"GET": [["notifications.push:get_client_token"]],
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationPushUserViewSet(
|
||||||
|
# mixins.RetrieveModelMixin,
|
||||||
|
mixins.ListModelMixin,
|
||||||
|
# mixins.UpdateModelMixin,
|
||||||
|
# mixins.DestroyModelMixin,
|
||||||
|
mixins.CreateModelMixin,
|
||||||
|
GenericViewSet
|
||||||
|
):
|
||||||
|
queryset = PushMessage.objects.all()
|
||||||
|
serializer_class = PushMessageSerializer
|
||||||
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
||||||
|
required_alternate_scopes = {
|
||||||
|
"GET": [["notifications.application.push:submit_message"]],
|
||||||
|
"POST": [["notifications.application.push:submit_message"]],
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
user_uuid = self.kwargs.get('user_uuid')
|
||||||
|
push_message = get_object_or_404(queryset, user=user_uuid)
|
||||||
|
return push_message
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
user_uuid = self.kwargs.get('user_uuid')
|
||||||
|
user, _ = User.objects.get_or_create(uuid=user_uuid)
|
||||||
|
push_user = PushUser.objects.submit(user)
|
||||||
|
application = get_application(self.request)
|
||||||
|
instance = serializer.save(push_user=push_user, application=application)
|
||||||
|
instance.send_push()
|
||||||
|
|
@ -54,7 +54,7 @@ INSTALLED_APPS = [
|
||||||
'apps.core',
|
'apps.core',
|
||||||
'apps.users',
|
'apps.users',
|
||||||
'apps.gooyal_oauth2',
|
'apps.gooyal_oauth2',
|
||||||
'apps.notifications',
|
'apps.push_notifications',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
@ -241,37 +241,37 @@ CACHES = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGING = {
|
# LOGGING = {
|
||||||
'version': 1,
|
# 'version': 1,
|
||||||
'disable_existing_loggers': False,
|
# 'disable_existing_loggers': False,
|
||||||
'formatters': {
|
# 'formatters': {
|
||||||
'verbose': {
|
# 'verbose': {
|
||||||
'format': '{levelname} AT: {asctime} LINE: {lineno} LOGGER: {name} FILE: {pathname} MESSAGE: {message}',
|
# 'format': '{levelname} AT: {asctime} LINE: {lineno} LOGGER: {name} FILE: {pathname} MESSAGE: {message}',
|
||||||
'style': '{',
|
# 'style': '{',
|
||||||
},
|
# },
|
||||||
'simple': {
|
# 'simple': {
|
||||||
'format': '{levelname} {message}',
|
# 'format': '{levelname} {message}',
|
||||||
'style': '{',
|
# 'style': '{',
|
||||||
},
|
# },
|
||||||
},
|
# },
|
||||||
'handlers': {
|
# 'handlers': {
|
||||||
'file': {
|
# 'file': {
|
||||||
'level': 'DEBUG',
|
# 'level': 'DEBUG',
|
||||||
'class': 'logging.handlers.TimedRotatingFileHandler',
|
# 'class': 'logging.handlers.TimedRotatingFileHandler',
|
||||||
'filename': 'logs/app.log',
|
# 'filename': 'logs/app.log',
|
||||||
'formatter': 'verbose',
|
# 'formatter': 'verbose',
|
||||||
"when": 'D',
|
# "when": 'D',
|
||||||
"backupCount": 5,
|
# "backupCount": 5,
|
||||||
},
|
# },
|
||||||
},
|
# },
|
||||||
'loggers': {
|
# 'loggers': {
|
||||||
'root': {
|
# 'root': {
|
||||||
'handlers': ['file'],
|
# 'handlers': ['file'],
|
||||||
'level': 'DEBUG',
|
# 'level': 'DEBUG',
|
||||||
'propagate': True,
|
# 'propagate': True,
|
||||||
},
|
# },
|
||||||
},
|
# },
|
||||||
}
|
# }
|
||||||
|
|
||||||
# from datetime import timedelta
|
# from datetime import timedelta
|
||||||
# from typing import List, Tuple
|
# from typing import List, Tuple
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@ urlpatterns = [
|
||||||
|
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('', include('apps.core.urls')),
|
path('', include('apps.core.urls')),
|
||||||
path('', include('apps.notifications.urls')),
|
path('push/user/', include('apps.push_notifications.user_urls')),
|
||||||
|
path('push/application/<user_uuid>/', include('apps.push_notifications.application_urls')),
|
||||||
# path('users/', include('apps.users.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')),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import time
|
import time
|
||||||
|
from django.utils import timezone
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
@ -14,6 +15,7 @@ from gotify_rest_api_client.api.message import create_message
|
||||||
from gotify_rest_api_client.api.user import create_user
|
from gotify_rest_api_client.api.user import create_user
|
||||||
from gotify_rest_api_client.api.client import create_client
|
from gotify_rest_api_client.api.client import create_client
|
||||||
from gotify_rest_api_client.api.application import create_app
|
from gotify_rest_api_client.api.application import create_app
|
||||||
|
from gotify_rest_api_client.types import Unset
|
||||||
|
|
||||||
ADMIN_CLIENT_TOKEN = 'CbWdsJrmRTDzbuD'
|
ADMIN_CLIENT_TOKEN = 'CbWdsJrmRTDzbuD'
|
||||||
|
|
||||||
|
|
@ -62,30 +64,19 @@ def get_basic_client(username, password):
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
def send_notif(data):
|
def send_notif(token, title=None, message=None, priority=None, extras=None):
|
||||||
class Tmp():
|
extras = extras or Unset()
|
||||||
def to_dict(self):
|
final_data = MessageExternalModel(appid=1, date=timezone.now(), id=1, title=title, message=message, priority=priority, extras=extras)
|
||||||
return data
|
client = get_client(access_token=token)
|
||||||
|
|
||||||
final_data = Tmp()
|
|
||||||
client = get_client()
|
|
||||||
response: Any = create_message.sync(client=client, body=final_data)
|
response: Any = create_message.sync(client=client, body=final_data)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def submit_user(user_uuid):
|
def submit_user(user_uuid):
|
||||||
data_dict = {
|
final_data = CreateUserExternalModel(admin=False,
|
||||||
"admin": False,
|
name=user_uuid,
|
||||||
"name": user_uuid,
|
pass_=user_uuid)
|
||||||
"pass": user_uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
class Tmp():
|
|
||||||
def to_dict(self):
|
|
||||||
return data_dict
|
|
||||||
|
|
||||||
final_data = Tmp()
|
|
||||||
client = get_client(ADMIN_CLIENT_TOKEN)
|
client = get_client(ADMIN_CLIENT_TOKEN)
|
||||||
response: UserExternalModel = create_user.sync(client=client, body=final_data)
|
response: UserExternalModel = create_user.sync(client=client, body=final_data)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue