more utils
This commit is contained in:
parent
68f0dd13db
commit
7a7f58ba69
20 changed files with 410 additions and 170 deletions
0
apps/sample/__init__.py
Normal file
0
apps/sample/__init__.py
Normal file
9
apps/sample/admin.py
Normal file
9
apps/sample/admin.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
from functools import update_wrapper
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import Sample
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Sample)
|
||||||
|
|
||||||
6
apps/sample/apps.py
Normal file
6
apps/sample/apps.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class SampleConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.sample'
|
||||||
11
apps/sample/filters.py
Normal file
11
apps/sample/filters.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import django_filters
|
||||||
|
|
||||||
|
from .models import Sample
|
||||||
|
|
||||||
|
|
||||||
|
class SampleFilter(django_filters.FilterSet):
|
||||||
|
class Meta:
|
||||||
|
model = Sample
|
||||||
|
fields = {
|
||||||
|
'data': ['exact'],
|
||||||
|
}
|
||||||
12
apps/sample/forms.py
Normal file
12
apps/sample/forms.py
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from .models import Sample
|
||||||
|
|
||||||
|
|
||||||
|
class SampleForm(forms.ModelForm):
|
||||||
|
# state = forms.ChoiceField(required=False, disabled=True)
|
||||||
|
class Meta:
|
||||||
|
model = Sample
|
||||||
|
fields = ['data', 'user', 'application']
|
||||||
|
readonly_fields = ['user', 'application']
|
||||||
0
apps/sample/migrations/__init__.py
Normal file
0
apps/sample/migrations/__init__.py
Normal file
21
apps/sample/models.py
Normal file
21
apps/sample/models.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import models
|
||||||
|
from oauth2_provider.settings import oauth2_settings
|
||||||
|
|
||||||
|
from utils.models import BaseModel
|
||||||
|
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class SampleQuerySet(models.QuerySet):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Sample(BaseModel):
|
||||||
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
||||||
|
application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT,
|
||||||
|
related_name='+', null=True, blank=True)
|
||||||
|
|
||||||
|
data = models.JSONField(null=True, blank=True)
|
||||||
|
|
||||||
|
objects = SampleQuerySet.as_manager()
|
||||||
14
apps/sample/serializers.py
Normal file
14
apps/sample/serializers.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Sample
|
||||||
|
|
||||||
|
|
||||||
|
class SampleSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Sample
|
||||||
|
fields = (
|
||||||
|
"user",
|
||||||
|
"application",
|
||||||
|
"data"
|
||||||
|
)
|
||||||
|
read_only_fields = ('user','application')
|
||||||
7
apps/sample/tasks.py
Normal file
7
apps/sample/tasks.py
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
from main import celery_app
|
||||||
|
|
||||||
|
|
||||||
|
@celery_app.task
|
||||||
|
def sample_application_task(sample_uuid):
|
||||||
|
from .models import Sample
|
||||||
|
sample = Sample.objects.get(uuid=sample_uuid)
|
||||||
3
apps/sample/tests.py
Normal file
3
apps/sample/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
19
apps/sample/urls.py
Normal file
19
apps/sample/urls.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
from rest_framework import routers
|
||||||
|
from rest_framework.routers import DefaultRouter, DynamicRoute, Route
|
||||||
|
|
||||||
|
from utils.router import ProfileRouter
|
||||||
|
from . import views
|
||||||
|
from django.urls import NoReverseMatch, path, re_path, include
|
||||||
|
|
||||||
|
app_name = 'sample'
|
||||||
|
|
||||||
|
application_router = DefaultRouter()
|
||||||
|
application_router.register(r'', views.ApplicationSampleViewSet, basename='application' )
|
||||||
|
|
||||||
|
user_router = ProfileRouter()
|
||||||
|
user_router.register('', views.UserSampleViewSet, basename='user')
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('api/v1/application/<user_uuid>/', include(application_router.urls), name='application'),
|
||||||
|
path('api/v1/user/', include(user_router.urls), name='user'),
|
||||||
|
]
|
||||||
63
apps/sample/views.py
Normal file
63
apps/sample/views.py
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
from django.http import Http404
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.urls import reverse
|
||||||
|
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 Sample
|
||||||
|
from .serializers import SampleSerializer
|
||||||
|
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 ApplicationSampleViewSet(mixins.ListModelMixin,
|
||||||
|
mixins.CreateModelMixin,
|
||||||
|
GenericViewSet):
|
||||||
|
queryset = Sample.objects.all()
|
||||||
|
serializer_class = SampleSerializer
|
||||||
|
|
||||||
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
||||||
|
required_alternate_scopes = {
|
||||||
|
"POST": [["service_template.application.user-sample:submit"]],
|
||||||
|
"GET": [["service_template.application.user-sample:list-retrieve"]],
|
||||||
|
}
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
user_uuid = self.kwargs.get('user_uuid')
|
||||||
|
user = User.objects.get(uuid=user_uuid)
|
||||||
|
application = get_application(self.request)
|
||||||
|
instance = serializer.save(user=user, application=application)
|
||||||
|
|
||||||
|
|
||||||
|
class UserSampleViewSet(mixins.RetrieveModelMixin,
|
||||||
|
GenericViewSet):
|
||||||
|
queryset = Sample.objects.all()
|
||||||
|
serializer_class = SampleSerializer
|
||||||
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
||||||
|
required_alternate_scopes = {
|
||||||
|
"GET": [["service_template.user.self-sample:list-retrieve"]],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,16 +1,42 @@
|
||||||
from .base_settings import *
|
"""
|
||||||
from django.utils.translation import gettext_lazy as _
|
Django settings for main project.
|
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 5.1.4.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.1/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
from decouple import config
|
from decouple import config
|
||||||
|
|
||||||
SECRET_KEY = config('SECRET_KEY', default=SECRET_KEY)
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = config('SECRET_KEY', default='django-insecure-ync*tk)t*bz3zzbza&xvw6cl+wk6+@gk^@)2)-pp=^&v%@knib')
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = config('DEBUG', default=DEBUG, cast=bool)
|
DEBUG = config('DEBUG', default=True, cast=bool)
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = ['*']
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
INSTALLED_APPS += [
|
|
||||||
# pip installed apps
|
# pip installed apps
|
||||||
'drf_spectacular',
|
'drf_spectacular',
|
||||||
'oauth2_provider',
|
'oauth2_provider',
|
||||||
|
|
@ -27,6 +53,7 @@ INSTALLED_APPS += [
|
||||||
'utils',
|
'utils',
|
||||||
'apps.users',
|
'apps.users',
|
||||||
'apps.gooyal_oauth2',
|
'apps.gooyal_oauth2',
|
||||||
|
'apps.activity',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
@ -40,7 +67,83 @@ MIDDLEWARE = [
|
||||||
'oauth2_provider.middleware.OAuth2TokenMiddleware',
|
'oauth2_provider.middleware.OAuth2TokenMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
'utils.exceptions.ErrorMiddleware',
|
||||||
]
|
]
|
||||||
|
ROOT_URLCONF = 'main.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [BASE_DIR / 'templates'],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'main.wsgi.application'
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
"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
|
||||||
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/5.1/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
|
TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = 'static/'
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
#######################
|
||||||
|
|
||||||
|
|
||||||
OAUTH2_PROVIDER_APPLICATION_MODEL = "gooyal_oauth2.Application"
|
OAUTH2_PROVIDER_APPLICATION_MODEL = "gooyal_oauth2.Application"
|
||||||
OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "gooyal_oauth2.AccessToken"
|
OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "gooyal_oauth2.AccessToken"
|
||||||
|
|
@ -48,12 +151,12 @@ OAUTH2_PROVIDER_ID_TOKEN_MODEL = "gooyal_oauth2.IDToken"
|
||||||
OAUTH2_PROVIDER_GRANT_MODEL = "gooyal_oauth2.Grant"
|
OAUTH2_PROVIDER_GRANT_MODEL = "gooyal_oauth2.Grant"
|
||||||
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "gooyal_oauth2.RefreshToken"
|
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "gooyal_oauth2.RefreshToken"
|
||||||
|
|
||||||
BASE_OAUTH2_PROVIDER_PUBLIC_URL = config("BASE_OAUTH2_PROVIDER_PUBLIC_URL")
|
OAUTH2_PROVIDER_PUBLIC_URL = config("OAUTH2_PROVIDER_PUBLIC_URL")
|
||||||
BASE_OAUTH2_PROVIDER_PRIVATE_URL = config("BASE_OAUTH2_PROVIDER_PRIVATE_URL")
|
OAUTH2_PROVIDER_PRIVATE_URL = config("OAUTH2_PROVIDER_PRIVATE_URL")
|
||||||
|
|
||||||
CLIENT_ID = config('CLIENT_ID')
|
OAUTH2_CLIENT_ID = config('OAUTH2_CLIENT_ID')
|
||||||
CLIENT_SECRET = config('CLIENT_SECRET')
|
OAUTH2_CLIENT_SECRET = config('OAUTH2_CLIENT_SECRET')
|
||||||
SCOPES = config('SCOPES', default='')
|
OAUTH2_SCOPES = config('OAUTH2_SCOPES', default='')
|
||||||
OAUTH2_PROVIDER = {
|
OAUTH2_PROVIDER = {
|
||||||
# this is the list of available scopes
|
# this is the list of available scopes
|
||||||
# 'SCOPES_BACKEND_CLASS': 'apps.gooyal_oauth2.scopes.Scopes',
|
# 'SCOPES_BACKEND_CLASS': 'apps.gooyal_oauth2.scopes.Scopes',
|
||||||
|
|
@ -62,9 +165,9 @@ OAUTH2_PROVIDER = {
|
||||||
# 'groups': 'Access to your groups',
|
# 'groups': 'Access to your groups',
|
||||||
# 'introspection': 'Introspect token scope'},
|
# 'introspection': 'Introspect token scope'},
|
||||||
'OAUTH2_VALIDATOR_CLASS': 'apps.gooyal_oauth2.validators.OAuth2Validator',
|
'OAUTH2_VALIDATOR_CLASS': 'apps.gooyal_oauth2.validators.OAuth2Validator',
|
||||||
'RESOURCE_SERVER_INTROSPECTION_URL': BASE_OAUTH2_PROVIDER_PRIVATE_URL + '/introspect/',
|
'RESOURCE_SERVER_INTROSPECTION_URL': OAUTH2_PROVIDER_PRIVATE_URL + '/introspect/',
|
||||||
# 'RESOURCE_SERVER_AUTH_TOKEN': '3yUqsWtwKYKHnfivFcJu', # OR this but not both:
|
# 'RESOURCE_SERVER_AUTH_TOKEN': '3yUqsWtwKYKHnfivFcJu', # OR this but not both:
|
||||||
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': (CLIENT_ID, CLIENT_SECRET),
|
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': (OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET),
|
||||||
}
|
}
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
|
|
@ -84,41 +187,24 @@ REST_FRAMEWORK = {
|
||||||
'DEFAULT_THROTTLE_RATES': {
|
'DEFAULT_THROTTLE_RATES': {
|
||||||
# 'otp_min': '1/min',
|
# 'otp_min': '1/min',
|
||||||
# 'otp_day': '20/day',
|
# 'otp_day': '20/day',
|
||||||
}
|
},
|
||||||
|
'EXCEPTION_HANDLER': 'utils.exceptions.ExceptionHandler',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEMPLATES[0]['DIRS'] += [BASE_DIR / 'templates']
|
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = (
|
AUTHENTICATION_BACKENDS = (
|
||||||
'oauth2_provider.backends.OAuth2Backend',
|
'oauth2_provider.backends.OAuth2Backend',
|
||||||
'django.contrib.auth.backends.ModelBackend',
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
"ENGINE": "django.contrib.gis.db.backends.postgis",
|
|
||||||
'NAME': config('DB_NAME'),
|
|
||||||
'USER': config('DB_USER'),
|
|
||||||
'PASSWORD': config('DB_PASSWORD'),
|
|
||||||
'HOST': config('DB_HOST', '127.0.0.1'),
|
|
||||||
'PORT': config('DB_PORT', ''),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
|
|
||||||
|
|
||||||
LOCALE_PATHS = [
|
LOCALE_PATHS = [
|
||||||
BASE_DIR / 'locale',
|
BASE_DIR / 'locale',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
MEDIA_URL = 'media/'
|
MEDIA_URL = 'media/'
|
||||||
MEDIA_ROOT = BASE_DIR / 'media'
|
MEDIA_ROOT = BASE_DIR / 'media'
|
||||||
|
|
||||||
|
|
||||||
if DEBUG == True:
|
if DEBUG == True:
|
||||||
STATICFILES_DIRS = (
|
STATICFILES_DIRS = (
|
||||||
BASE_DIR / 'static',
|
BASE_DIR / 'static',
|
||||||
|
|
@ -126,8 +212,6 @@ if DEBUG == True:
|
||||||
else:
|
else:
|
||||||
STATIC_ROOT = BASE_DIR / 'static'
|
STATIC_ROOT = BASE_DIR / 'static'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LOGIN_URL = '/users/login/request/'
|
LOGIN_URL = '/users/login/request/'
|
||||||
LOGIN_REDIRECT_URL = '/'
|
LOGIN_REDIRECT_URL = '/'
|
||||||
|
|
||||||
|
|
@ -135,7 +219,7 @@ CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||||
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||||
|
|
||||||
SPECTACULAR_SETTINGS = {
|
SPECTACULAR_SETTINGS = {
|
||||||
'TITLE': 'merchants',
|
'TITLE': 'service',
|
||||||
'DESCRIPTION': 'service api reference',
|
'DESCRIPTION': 'service api reference',
|
||||||
'VERSION': '1.0.0',
|
'VERSION': '1.0.0',
|
||||||
'SERVE_INCLUDE_SCHEMA': False,
|
'SERVE_INCLUDE_SCHEMA': False,
|
||||||
|
|
@ -191,8 +275,10 @@ LOGGING = {
|
||||||
'timeout': 1, # Post request timeout, default is 0.5. Optional
|
'timeout': 1, # Post request timeout, default is 0.5. Optional
|
||||||
'url': 'https://loki.winsoo.ir/loki/api/v1/push', # Loki url. Defaults to localhost. Optional.
|
'url': 'https://loki.winsoo.ir/loki/api/v1/push', # Loki url. Defaults to localhost. Optional.
|
||||||
# 'auth': ("user", "password"), # Basic auth to authenticate with loki. Default is None (i.e. no auth). Optional
|
# 'auth': ("user", "password"), # Basic auth to authenticate with loki. Default is None (i.e. no auth). Optional
|
||||||
'tags': { "app": "ads", "env": "production" }, # Tags / Labels to attach to the log. Optional, but strongly encoraged to use.
|
'tags': {"app": "ads", "env": "production"},
|
||||||
'mode': 'thread', # Push mode. Can be 'sync' or 'thread'. Sync is blocking, thread is non-blocking. Defaults to sync. Optional.
|
# Tags / Labels to attach to the log. Optional, but strongly encoraged to use.
|
||||||
|
'mode': 'thread',
|
||||||
|
# Push mode. Can be 'sync' or 'thread'. Sync is blocking, thread is non-blocking. Defaults to sync. Optional.
|
||||||
},
|
},
|
||||||
# 'file': {
|
# 'file': {
|
||||||
# 'level': 'DEBUG',
|
# 'level': 'DEBUG',
|
||||||
|
|
@ -233,8 +319,10 @@ STORAGES = {
|
||||||
MINIO_ENDPOINT = config('MINIO_ENDPOINT', default='drive.gooyal.com')
|
MINIO_ENDPOINT = config('MINIO_ENDPOINT', default='drive.gooyal.com')
|
||||||
MINIO_USE_HTTPS = config('MINIO_USE_HTTPS', default=True, cast=bool)
|
MINIO_USE_HTTPS = config('MINIO_USE_HTTPS', default=True, cast=bool)
|
||||||
|
|
||||||
MINIO_EXTERNAL_ENDPOINT = config('MINIO_EXTERNAL_ENDPOINT', default='drive.gooyal.com') # Default is same as MINIO_ENDPOINT
|
MINIO_EXTERNAL_ENDPOINT = config('MINIO_EXTERNAL_ENDPOINT',
|
||||||
MINIO_EXTERNAL_ENDPOINT_USE_HTTPS = config('MINIO_EXTERNAL_ENDPOINT_USE_HTTPS', default=True, cast=bool) # Default is same as MINIO_USE_HTTPS
|
default='drive.gooyal.com') # Default is same as MINIO_ENDPOINT
|
||||||
|
MINIO_EXTERNAL_ENDPOINT_USE_HTTPS = config('MINIO_EXTERNAL_ENDPOINT_USE_HTTPS', default=True,
|
||||||
|
cast=bool) # Default is same as MINIO_USE_HTTPS
|
||||||
MINIO_REGION = None # Default is set to None
|
MINIO_REGION = None # Default is set to None
|
||||||
MINIO_ACCESS_KEY = config('MINIO_ACCESS_KEY')
|
MINIO_ACCESS_KEY = config('MINIO_ACCESS_KEY')
|
||||||
MINIO_SECRET_KEY = config('MINIO_SECRET_KEY')
|
MINIO_SECRET_KEY = config('MINIO_SECRET_KEY')
|
||||||
|
|
@ -272,14 +360,11 @@ MINIO_HTTP_CLIENT: urllib3.poolmanager.PoolManager = urllib3.PoolManager(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
REDIS_BASE_URL = config('REDIS_BASE_URL')
|
REDIS_BASE_URL = config('REDIS_BASE_URL')
|
||||||
|
|
||||||
|
CELERY = {
|
||||||
|
"broker_url": REDIS_BASE_URL,
|
||||||
CELERY={
|
"result_backend": REDIS_BASE_URL,
|
||||||
"broker_url" :REDIS_BASE_URL,
|
|
||||||
"result_backend" : REDIS_BASE_URL,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CELERY_TASK_TRACK_STARTED = True
|
CELERY_TASK_TRACK_STARTED = True
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
from .base_settings import *
|
|
||||||
from .settings import *
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
"""
|
|
||||||
Django settings for main project.
|
|
||||||
|
|
||||||
Generated by 'django-admin startproject' using Django 5.1.4.
|
|
||||||
|
|
||||||
For more information on this file, see
|
|
||||||
https://docs.djangoproject.com/en/5.1/topics/settings/
|
|
||||||
|
|
||||||
For the full list of settings and their values, see
|
|
||||||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
|
||||||
"""
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = 'django-insecure-ync*tk)t*bz3zzbza&xvw6cl+wk6+@gk^@)2)-pp=^&v%@knib'
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
]
|
|
||||||
|
|
||||||
MIDDLEWARE = [
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
]
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'main.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'main.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
'NAME': BASE_DIR / 'db.sqlite3',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/5.1/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
|
||||||
|
|
||||||
# Default primary key field type
|
|
||||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
||||||
54
utils/exceptions.py
Normal file
54
utils/exceptions.py
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from rest_framework.exceptions import APIException
|
||||||
|
from rest_framework.views import exception_handler as drf_exception_handler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class ExceptionHandler:
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __call__(cls, exc, context):
|
||||||
|
# پاسخ پیشفرض DRF را دریافت میکنیم
|
||||||
|
response = drf_exception_handler(exc, context)
|
||||||
|
|
||||||
|
if response is not None:
|
||||||
|
# ساختار دلخواه خود را تعریف میکنیم
|
||||||
|
response_data = {
|
||||||
|
'success': False,
|
||||||
|
'status_code': response.status_code,
|
||||||
|
'message': 'An error occurred',
|
||||||
|
'details': {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isinstance(exc, APIException):
|
||||||
|
response_data['message'] = exc.detail if isinstance(exc.detail, str) else "Validation error"
|
||||||
|
response_data['details'] = exc.detail if isinstance(exc.detail, dict) else {}
|
||||||
|
|
||||||
|
response.data = response_data
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
# در فایل middleware.py
|
||||||
|
from django.http import JsonResponse
|
||||||
|
|
||||||
|
class ErrorMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
response = self.get_response(request)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def process_exception(self, request, exception):
|
||||||
|
logger.exception(exception)
|
||||||
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
"success": False,
|
||||||
|
"status_code": 500,
|
||||||
|
"message": "Internal server error",
|
||||||
|
"details": str(exception) # فقط در حالت توسعه! در تولید بهتر است لاگ شود.
|
||||||
|
},
|
||||||
|
status=500
|
||||||
|
)
|
||||||
14
utils/i18n.py
Normal file
14
utils/i18n.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
from django.utils import translation
|
||||||
|
|
||||||
|
class ForceAdminLanguageMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
if request.path.startswith('/admin/'):
|
||||||
|
translation.activate('en')
|
||||||
|
request.LANGUAGE_CODE = 'en'
|
||||||
|
else:
|
||||||
|
translation.activate('fa')
|
||||||
|
request.LANGUAGE_CODE = 'fa'
|
||||||
|
return self.get_response(request)
|
||||||
47
utils/router.py
Normal file
47
utils/router.py
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
from rest_framework.routers import DefaultRouter, Route, DynamicRoute
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileRouter(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={}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue