diff --git a/Rasaddam_Backend/settings.py b/Rasaddam_Backend/settings.py index 85405d0..a4a35e7 100644 --- a/Rasaddam_Backend/settings.py +++ b/Rasaddam_Backend/settings.py @@ -26,7 +26,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent SECRET_KEY = 'django-insecure-@0apn-lk85pfw=z00x2ib$w9#rwz8%2v4i_n^^9jz-m9b+y55*' # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = False sentry_sdk.init( dsn="https://e8d8ff4f1bf729370af00b7775be441c@o4509597964697600.ingest.us.sentry.io/4509597966073856", @@ -98,6 +98,7 @@ INSTALLED_APPS = [ MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", 'django.middleware.security.SecurityMiddleware', + 'apps.core.middlewares.Json500Middleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -110,7 +111,7 @@ MIDDLEWARE = [ 'apps.pos_device.middlewares.PosDeviceValidationMiddleware' ] -ROOT_URLCONF = 'Rasaddam_Backend.urls' +ROOT_URLCONF = 'Rasaddam_Backend.urls' # noqa TEMPLATES = [ { @@ -196,7 +197,7 @@ REST_FRAMEWORK = { 'django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.SearchFilter', ], - # 'EXCEPTION_HANDLER': 'apps.core.error_handler.custom_exception_handler', + 'EXCEPTION_HANDLER': 'apps.core.error_handler.custom_exception_handler', "DEFAULT_PAGINATION_CLASS": 'apps.core.pagination.CustomPageNumberPagination', "PAGE_SIZE": 20, 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' @@ -261,7 +262,7 @@ CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { - "hosts": [("redis://:ydnW4hwzuDRYcTX3FWCHgQ1f@apo.liara.cloud:33740/0")], + "hosts": [("redis://:ydnW4hwzuDRYcTX3FWCHgQ1f@apo.liara.cloud:33740/0")], # noqa }, }, } @@ -286,8 +287,8 @@ ELASTICSEARCH_DSL = { # liara elastic password uYkiQ860vLW8DIbWpNjqtz2B # noqa # local system password =z66+LCIebq4NQRR_+=R # noqa "default": { - "hosts": "http://monte-rosa.liara.cloud:31157", - "http_auth": ("elastic", "uYkiQ860vLW8DIbWpNjqtz2B"), + "hosts": "http://monte-rosa.liara.cloud:31157", # noqa + "http_auth": ("elastic", "uYkiQ860vLW8DIbWpNjqtz2B"), # noqa } } @@ -450,7 +451,6 @@ JAZZMIN_SETTINGS = { }] }, - # Custom icons for side menu apps/models See https://fontawesome.com/icons?d=gallery&m=free&v=5.0.0,5.0.1,5.0.10,5.0.11,5.0.12,5.0.13,5.0.2,5.0.3,5.0.4,5.0.5,5.0.6,5.0.7,5.0.8,5.0.9,5.1.0,5.1.1,5.2.0,5.3.0,5.3.1,5.4.0,5.4.1,5.4.2,5.13.0,5.12.0,5.11.2,5.11.1,5.10.0,5.9.0,5.8.2,5.8.1,5.7.2,5.7.1,5.7.0,5.6.3,5.5.0,5.4.2 # for the full list of 5.13.0 free icon classes "icons": { "auth": "fas fa-users-cog", diff --git a/apps/core/error_handler.py b/apps/core/error_handler.py index b87b648..96afa1d 100644 --- a/apps/core/error_handler.py +++ b/apps/core/error_handler.py @@ -1,5 +1,7 @@ from django.http import JsonResponse from rest_framework.views import exception_handler +from django.conf import settings +import traceback def custom_exception_handler(exc, context): @@ -11,6 +13,13 @@ def custom_exception_handler(exc, context): response.data['message'] = response.data.get('detail', str(exc)) del response.data['detail'] else: - response = JsonResponse({'message': str(exc), 'status_code': 500}) + if settings.DEBUG: + raise + response = JsonResponse({ + "message": str(exc), + "status_code": 500, + "error_type": exc.__class__.__name__, + "traceback": traceback.format_exc().splitlines(), + }, status=500) response.status_code = 500 return response diff --git a/apps/core/middlewares.py b/apps/core/middlewares.py index 9c4b56c..92b878f 100644 --- a/apps/core/middlewares.py +++ b/apps/core/middlewares.py @@ -1,60 +1,29 @@ from rest_framework.exceptions import APIException +from django.http import JsonResponse from rest_framework import status from django.db import connection from django.conf import settings +import traceback import os -def terminal_width(): - """ - Function to compute the terminal width. - WARNING: This is not my code, but I've been using it forever and - I don't remember where it came from. - """ - width = 0 - try: - import struct, fcntl, termios - s = struct.pack('HHHH', 0, 0, 0, 0) - x = fcntl.ioctl(1, termios.TIOCGWINSZ, s) - width = struct.unpack('HHHH', x)[1] - except: - pass - if width <= 0: - try: - width = int(os.environ['COLUMNS']) - except: - pass - if width <= 0: - width = 80 - return width - - -class SqlPrintingMiddleware(object): - """ - Middleware which prints out a list of all SQL queries done - for each view that is processed. This is only useful for debugging. - """ +class Json500Middleware: + """ return all 500 status errors in json response """ def __init__(self, get_response): self.get_response = get_response - def __call__(self, request): - return self.process_response(request=request, response=self.get_response(request)) + def __call__(self, request, *args, **kwargs): + try: + return self.get_response(request) + except Exception as exc: - def process_response(self, request, response): # noqa - indentation = 2 - if len(connection.queries) > 0 and settings.DEBUG: - width = terminal_width() - total_time = 0.0 - for query in connection.queries: - nice_sql = query['sql'].replace('"', '').replace(',', ', ') - sql = "\033[1;31m[%s]\033[0m %s" % (query['time'], nice_sql) - total_time = total_time + float(query['time']) - while len(sql) > width - indentation: - print("%s%s" % (" " * indentation, sql[:width - indentation])) - sql = sql[width - indentation:] - print("%s%s\n" % (" " * indentation, sql)) - replace_tuple = (" " * indentation, str(total_time)) - print("%s\033[1;32m[TOTAL TIME: %s seconds]\033[0m" % replace_tuple) - print(response) - return response + if settings.DEBUG: + raise + + return JsonResponse({ + "message": str(exc), + "status_code": 500, + "error_type": exc.__class__.__name__, + "traceback": traceback.format_exc().splitlines(), + }, status=500) diff --git a/apps/herd/pos/api/v1/api.py b/apps/herd/pos/api/v1/api.py index 6a91a2b..cfdd381 100644 --- a/apps/herd/pos/api/v1/api.py +++ b/apps/herd/pos/api/v1/api.py @@ -1,10 +1,5 @@ -from apps.herd.web.api.v1.serializers import HerdSerializer, RancherSerializer +from apps.herd.pos.api.v1.serializers import HerdSerializer, RancherSerializer from apps.livestock.web.api.v1.serializers import LiveStockSerializer -from apps.herd.services.services import ( - get_rancher_statistics, - rancher_quota_weight -) -from apps.warehouse.models import InventoryEntry from apps.core.mixins.search_mixin import DynamicSearchMixin from rest_framework.exceptions import APIException from rest_framework.permissions import AllowAny diff --git a/apps/herd/pos/api/v1/serializers.py b/apps/herd/pos/api/v1/serializers.py index e3b2ea6..3d5f375 100644 --- a/apps/herd/pos/api/v1/serializers.py +++ b/apps/herd/pos/api/v1/serializers.py @@ -42,6 +42,8 @@ class RancherSerializer(serializers.ModelSerializer): if self.Meta.model.objects.filter(national_code=attrs['national_code']).exists(): raise UniqueRancherApiException() + return attrs + def to_representation(self, instance): """ customize output of serializer """ diff --git a/apps/product/exceptions.py b/apps/product/exceptions.py index 9bf5daa..bd1f0fc 100644 --- a/apps/product/exceptions.py +++ b/apps/product/exceptions.py @@ -35,3 +35,13 @@ class QuotaLimitByOrganizationException(APIException): status_code = status.HTTP_400_BAD_REQUEST default_detail = "سازمان انتخاب شده در بین سامان های انتخاب شده برای توزیع سهمیه وجود ندارد" # noqa default_code = 'error' + + +class FreePOSProductUniqueCheck(APIException): + """ + raise exception for free pos products to be unique + """ + + status_code = status.HTTP_403_FORBIDDEN + default_detail = "محصول مورد نظر برای این دستگاه از قبل ثبت شده است" # noqa + default_code = 'error' diff --git a/apps/product/pos/api/v1/serializers/product_serializers.py b/apps/product/pos/api/v1/serializers/product_serializers.py index 63256fa..eba609c 100644 --- a/apps/product/pos/api/v1/serializers/product_serializers.py +++ b/apps/product/pos/api/v1/serializers/product_serializers.py @@ -1,8 +1,8 @@ from rest_framework import serializers from apps.product import models as product_models from apps.pos_device.models import POSFreeProducts -from apps.authorization.api.v1 import serializers as authorize_serializers -from apps.authentication.api.v1.serializers.serializer import OrganizationSerializer, OrganizationTypeSerializer +from apps.authentication.api.v1.serializers.serializer import OrganizationTypeSerializer +from apps.product.exceptions import FreePOSProductUniqueCheck class POSFreeProductSerializer(serializers.ModelSerializer): @@ -10,6 +10,18 @@ class POSFreeProductSerializer(serializers.ModelSerializer): model = POSFreeProducts fields = '__all__' + def validate(self, attrs): + """ + @check free product for pos to be unique + """ + organization = attrs['organization'] + product = attrs['product'] + + if self.Meta.model.objects.filter(organization=organization, product=product).exists(): + raise FreePOSProductUniqueCheck() + + return attrs + def to_representation(self, instance): representation = super().to_representation(instance)