add pos header validation middleware

This commit is contained in:
2025-07-26 15:50:57 +03:30
parent 9b8d4c0353
commit b425caf22f
4 changed files with 114 additions and 63 deletions

View File

@@ -0,0 +1,30 @@
from rest_framework.exceptions import APIException
from django.utils.timezone import now
from apps.pos_device.models import DeviceVersion, ProviderCompany, Sessions
class POSDeviceMiddleware:
REQUIRED_HEADERS = [
'device-id', 'device-mac', 'device-serial', 'device-name',
'device-sdk', 'device-provider', 'device-version',
'device-vname', 'device-lng', 'device-lot' # noqa
]
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request, *args, **kwargs):
pass
def is_post_request(self, request): # noqa
""" check if is pos request """
has_device_headers = request.headers.get('device-id') and request.headers.get('device-mac')
is_pos_api_path = request.path.startswith('/api/pos/')
return has_device_headers or is_pos_api_path
def validate_pos_request(self, request):
""" validate request headers from pos device """
data = {key: request.headers.get(key) for key in self.REQUIRED_HEADERS}

View File

@@ -1,66 +1,74 @@
import datetime
from django.http import JsonResponse
from django.utils.timezone import now
from rest_framework.exceptions import APIException
from apps.pos_device.models import Device, ProviderCompany, Sessions, DeviceVersion
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
# اگر از پروکسی استفاده می‌شود، IP اول را برمی‌گردانیم
ip = x_forwarded_for.split(',')[0]
else:
# در غیر این صورت از REMOTE_ADDR استفاده می‌کنیم
ip = request.META.get('REMOTE_ADDR')
return ip
forwarded = request.META.get('HTTP_X_FORWARDED_FOR')
return forwarded.split(',')[0] if forwarded else request.META.get('REMOTE_ADDR')
class PosDeviceValidator:
class DeviceValidator:
REQUIRED_HEADERS = [
'device-id', 'device-mac', 'device-serial', 'device-name',
'device-sdk', 'device-provider', 'device-version',
'device-vname', 'device-lng', 'device-lot' # noqa
]
def __init__(self, request):
self.request = request
self.headers = request.headers
self.device_id = request.headers.get('device-id')
self.device_mac = request.headers.get('device-mac')
self.device_name = request.headers.get('device-name')
self.device_sdk = request.headers.get('device-sdk')
self.device_serial = request.headers.get('device-serial')
self.device_provider = request.headers.get('device-provider')
self.device_version = request.headers.get('device-version')
self.device_version_name = request.headers.get('device-vname')
self.device_lng = request.headers.get('device-lng')
self.device_lot = request.headers.get('device-lot')
self.data = {key: self.headers.get(key) for key in self.REQUIRED_HEADERS}
self.errors = []
def validation_version(self):
if self.device_provider == "" or self.device_provider == None:
return JsonResponse({'result': 'پارامتر های ارسالی صحیح نمیباشد!'}, status=402)
def validate_required_headers(self):
missing = [key for key, value in self.data.items() if not value]
if missing:
raise APIException(
f'پارامترهای ارسالی ناقص هستند: {", ".join(missing)}', # noqa
code=400
)
return None
def validate_version(self):
provider_name = self.data['device-provider']
company = ProviderCompany.objects.filter(en_name=provider_name).first()
company = ProviderCompany.objects.filter(en_name=self.device_provider).first()
if not company:
return JsonResponse({'result': 'شرکت پرداخت الکترونیک پشتیبانی نمیشود!'}, status=402)
raise APIException('شرکت پرداخت الکترونیک پشتیبانی نمیشود!', code=402) # noqa
if not company.active:
return JsonResponse({'result': 'شرکت پرداخت الکترونیک توسط مدیریت مسدود شده است!'}, status=402)
version = DeviceVersion.objects.filter(company=company).order_by('code')
if not version:
return JsonResponse({'result': ' هیچ نسخه معتبری برای این شرکت پرداخت الکترونیک منتشر نشده است!'},
status=402)
raise APIException('شرکت پرداخت الکترونیک مسدود شده است!', code=402) # noqa
current_version = version.filter(code=self.device_version).first()
versions = DeviceVersion.objects.filter(company=company)
if not versions.exists():
raise APIException('هیچ نسخه‌ای برای این شرکت ثبت نشده است!', code=402) # noqa
current_version = versions.filter(code=self.data['device-version']).first()
if not current_version or current_version.remove:
return JsonResponse({'result': f'نسخه {self.device_version_name} منقضی شده است لطفا بروز رسانی کنید '},
status=402)
raise APIException(
f'نسخه {self.data["device-vname"]} منقضی شده است. لطفا بروزرسانی کنید.', # noqa
code=402
)
return None
def validation_device(self):
pos_session = Sessions.objects.filter(pos__pos_id=self.device_id, mac=self.device_mac).first()
if not pos_session:
def validate_device_session(self):
session = Sessions.objects.filter(
pos__pos_id=self.data['device-id'],
mac=self.data['device-mac']
).first()
if session:
session.session_last_seen_date = now()
session.lng = self.data['device-lng']
session.lot = self.data['device-lot']
session.version = self.data['device-version']
session.ip = get_client_ip(self.request)
session.save()
return session.pos.pos_id
return None
else:
pos_session.session_last_seen_date = datetime.datetime.now()
pos_session.lng = self.device_lng
pos_session.lot = self.device_lot
pos_session.version = self.device_version
pos_session.ip = get_client_ip(self.request)
pos_session.save()
return pos_session.pos.pos_id

View File

@@ -331,10 +331,9 @@ class QuotaViewSet(viewsets.ModelViewSet): # noqa
# paginate queryset
page = self.paginate_queryset(
self.queryset.filter(
Q(assigned_organizations=organization) |
Q(registerer_organization=organization),
Q(is_closed=True)
)
).order_by('-modify_date')
)
if page is not None:
serializer = self.get_serializer(page, many=True)

View File

@@ -1,6 +1,7 @@
from django.db.models import Q
from apps.product.web.api.v1.serializers import quota_distribution_serializers as distribution_serializers
from apps.core.pagination import CustomPageNumberPagination
from rest_framework.exceptions import APIException
from apps.product import models as product_models
from rest_framework.response import Response
@@ -9,6 +10,8 @@ from rest_framework import viewsets, filters
from rest_framework import status
from django.db import transaction
from common.helpers import get_organization_by_user
def trash(queryset, pk): # noqa
""" sent object to trash """
@@ -30,6 +33,7 @@ class QuotaDistributionViewSet(viewsets.ModelViewSet):
serializer_class = distribution_serializers.QuotaDistributionSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['']
CustomPageNumberPagination.page_size = 5
@transaction.atomic
def create(self, request, *args, **kwargs):
@@ -68,17 +72,27 @@ class QuotaDistributionViewSet(viewsets.ModelViewSet):
return Response(serializer.data)
# @action(
# methods=['get'],
# url_name='my_distributions',
# url_path='my_distributions',
# name='my_distributions'
# )
# def my_distributions(self, request):
#
# distributions = self.queryset.filter(
# Q(assigned_organization='')
# )
@action(
methods=['get'],
detail=False,
url_name='my_distributions',
url_path='my_distributions',
name='my_distributions'
)
def my_distributions(self, request):
""" list of my distributions """
organization = get_organization_by_user(request.user)
# paginate queryset
page = self.paginate_queryset(
self.queryset.filter(
Q(assigned_organization=organization) |
Q(assigner_organization=organization)
).order_by('-modify_date')
)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
@action(
methods=['put'],