auth service created

This commit is contained in:
A'zamov Samandar
2025-04-19 14:58:55 +05:00
parent a4b5833bbe
commit dd0da49eb9
127 changed files with 67 additions and 43 deletions

View File

@@ -0,0 +1,3 @@
from .celery import app
__all__ = ["app"]

View File

@@ -0,0 +1,12 @@
import os
from django.core.asgi import get_asgi_application
asgi_application = get_asgi_application()
from config.env import env # noqa
os.environ.setdefault("DJANGO_SETTINGS_MODULE", env("DJANGO_SETTINGS_MODULE"))
application = asgi_application

View File

@@ -0,0 +1,16 @@
"""
Celery configurations
"""
import os
import celery
from config.env import env
os.environ.setdefault("DJANGO_SETTINGS_MODULE", env("DJANGO_SETTINGS_MODULE"))
app = celery.Celery("config")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

View File

@@ -0,0 +1,11 @@
from .cache import * # noqa
from .celery import * # noqa
from .cron import * # noqa
from .jwt import * # noqa
from .logs import * # noqa
from .rest_framework import * # noqa
from .unfold import * # noqa
from .spectacular import * # noqa
from .storage import * # noqa

View File

@@ -0,0 +1,22 @@
from config.env import env
APPS = [
"cacheops",
"drf_spectacular",
"rest_framework",
"corsheaders",
"django_filters",
"django_redis",
"rest_framework_simplejwt",
"django_core",
"core.apps.accounts.apps.AccountsConfig",
]
if env.str("PROJECT_ENV") == "debug":
APPS += [
"silk",
]

View File

@@ -0,0 +1,26 @@
from config.env import env
CACHES = {
"default": {
"BACKEND": env.str("CACHE_BACKEND"),
"LOCATION": env.str("REDIS_URL"),
"TIMEOUT": env.str("CACHE_TIMEOUT"),
},
}
CACHE_MIDDLEWARE_SECONDS = env("CACHE_TIMEOUT")
CACHEOPS_REDIS = env.str("REDIS_URL")
CACHEOPS_DEFAULTS = {
"timeout": env.str("CACHE_TIMEOUT"),
}
CACHEOPS = {
# !NOTE: api => "you app name"
# "api.*": {
# "ops": "all", # Barcha turdagi so'rovlarni keshga olish
# "timeout": 60 * 5, # 5 daqiqa davomida saqlash
# },
}
CACHEOPS_DEGRADE_ON_FAILURE = True
CACHEOPS_ENABLED = env.bool("CACHE_ENABLED", False)

View File

@@ -0,0 +1,7 @@
CELERY_BEAT_SCHEDULE = {
# "test": {
# "task": "core.apps.home.tasks.demo.add",
# "schedule": 5.0,
# "args": (1, 2)
# },
}

View File

@@ -0,0 +1,8 @@
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("redis", 6379)],
},
},
}

View File

@@ -0,0 +1,147 @@
import os
from pathlib import Path
STATIC_URL = "/resources/static/"
MEDIA_URL = "/resources/media/"
MEDIA_ROOT = os.path.join(Path().parent.parent, "media")
customColorPalette = [
{"color": "hsl(4, 90%, 58%)", "label": "Red"},
{"color": "hsl(340, 82%, 52%)", "label": "Pink"},
{"color": "hsl(291, 64%, 42%)", "label": "Purple"},
{"color": "hsl(262, 52%, 47%)", "label": "Deep Purple"},
{"color": "hsl(231, 48%, 48%)", "label": "Indigo"},
{"color": "hsl(207, 90%, 54%)", "label": "Blue"},
]
CKEDITOR_5_CONFIGS = {
"default": {
"toolbar": [
"heading",
"|",
"bold",
"italic",
"link",
"bulletedList",
"numberedList",
"blockQuote",
"imageUpload",
],
},
"extends": {
"blockToolbar": [
"paragraph",
"heading1",
"heading2",
"heading3",
"|",
"bulletedList",
"numberedList",
"|",
"blockQuote",
],
"toolbar": [
"heading",
"|",
"outdent",
"indent",
"|",
"bold",
"italic",
"link",
"underline",
"strikethrough",
"code",
"subscript",
"superscript",
"highlight",
"|",
"codeBlock",
"sourceEditing",
"insertImage",
"bulletedList",
"numberedList",
"todoList",
"|",
"blockQuote",
"imageUpload",
"|",
"fontSize",
"fontFamily",
"fontColor",
"fontBackgroundColor",
"mediaEmbed",
"removeFormat",
"insertTable",
],
"image": {
"toolbar": [
"imageTextAlternative",
"|",
"imageStyle:alignLeft",
"imageStyle:alignRight",
"imageStyle:alignCenter",
"imageStyle:side",
"|",
],
"styles": [
"full",
"side",
"alignLeft",
"alignRight",
"alignCenter",
],
},
"table": {
"contentToolbar": [
"tableColumn",
"tableRow",
"mergeTableCells",
"tableProperties",
"tableCellProperties",
],
"tableProperties": {
"borderColors": customColorPalette,
"backgroundColors": customColorPalette,
},
"tableCellProperties": {
"borderColors": customColorPalette,
"backgroundColors": customColorPalette,
},
},
"heading": {
"options": [
{
"model": "paragraph",
"title": "Paragraph",
"class": "ck-heading_paragraph",
},
{
"model": "heading1",
"view": "h1",
"title": "Heading 1",
"class": "ck-heading_heading1",
},
{
"model": "heading2",
"view": "h2",
"title": "Heading 2",
"class": "ck-heading_heading2",
},
{
"model": "heading3",
"view": "h3",
"title": "Heading 3",
"class": "ck-heading_heading3",
},
]
},
},
"list": {
"properties": {
"styles": "true",
"startIndex": "true",
"reversed": "true",
}
},
}

View File

View File

@@ -0,0 +1,36 @@
from datetime import timedelta
from config.env import env
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
"REFRESH_TOKEN_LIFETIME": timedelta(days=30),
"ROTATE_REFRESH_TOKENS": False,
"BLACKLIST_AFTER_ROTATION": False,
"UPDATE_LAST_LOGIN": False,
"ALGORITHM": "HS256",
"SIGNING_KEY": env("JWT_KEY"),
"VERIFYING_KEY": "",
"AUDIENCE": None,
"ISSUER": None,
"JSON_ENCODER": None,
"JWK_URL": None,
"LEEWAY": 0,
"AUTH_HEADER_TYPES": ("Bearer",),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
"TOKEN_TYPE_CLAIM": "token_type",
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
"JTI_CLAIM": "jti",
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=60),
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=30),
"TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
"TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
"TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
"TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}

View File

@@ -0,0 +1,29 @@
# settings.py faylida
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "{levelname} {asctime} {module} {message}",
"style": "{",
},
},
"handlers": {
"daily_rotating_file": {
"level": "INFO",
"class": "logging.handlers.TimedRotatingFileHandler",
"filename": "resources/logs/django.log", # Fayl nomi (kunlik fayllar uchun avtomatik yoziladi)
"when": "midnight", # Har kecha log fayli yangilanadi
"backupCount": 30, # 30 kunlik loglar saqlanadi, 1 oydan keyin eski fayllar o'chiriladi
"formatter": "verbose",
},
},
"loggers": {
"django": {
"handlers": ["daily_rotating_file"],
"level": "INFO",
"propagate": True,
},
},
}

View File

@@ -0,0 +1 @@
MODULES = []

View File

@@ -0,0 +1,31 @@
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
PAGES = [
{
"seperator": False,
"items": [
{
"title": _("Home page"),
"icon": "home",
"link": reverse_lazy("admin:index"),
}
],
},
{
"title": _("Auth"),
"separator": True, # Top border
"items": [
{
"title": _("Users"),
"icon": "group",
"link": reverse_lazy("admin:http_user_changelist"),
},
{
"title": _("Group"),
"icon": "group",
"link": reverse_lazy("admin:auth_group_changelist"),
},
],
},
]

View File

@@ -0,0 +1,9 @@
from typing import Any, Union
REST_FRAMEWORK: Union[Any] = {
"DEFAULT_AUTHENTICATION_CLASSES": ("rest_framework_simplejwt.authentication.JWTAuthentication",),
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
"DEFAULT_PAGINATION_CLASS": "django_core.paginations.CustomPagination",
"PAGE_SIZE": 10,
}

View File

@@ -0,0 +1,31 @@
SPECTACULAR_SETTINGS = {
"TITLE": "Your Project API",
"DESCRIPTION": "Your project description",
"VERSION": "1.0.0",
"SERVE_INCLUDE_SCHEMA": False,
"CAMELIZE_NAMES": True,
"POSTPROCESSING_HOOKS": ["config.conf.spectacular.custom_postprocessing_hook"],
}
def custom_postprocessing_hook(result, generator, request, public):
"""
Customizes the API schema to wrap all responses in a standard format.
"""
for path, methods in result.get("paths", {}).items():
for method, operation in methods.items():
if "responses" in operation:
for status_code, response in operation["responses"].items():
if "content" in response:
for content_type, content in response["content"].items():
# Wrap original schema
original_schema = content.get("schema", {})
response["content"][content_type]["schema"] = {
"type": "object",
"properties": {
"status": {"type": "boolean", "example": True},
"data": original_schema,
},
"required": ["status", "data"],
}
return result

View File

@@ -0,0 +1,23 @@
from config.env import env
from core.utils.storage import Storage
AWS_ACCESS_KEY_ID = env.str("STORAGE_ID")
AWS_SECRET_ACCESS_KEY = env.str("STORAGE_KEY")
AWS_S3_ENDPOINT_URL = env.str("STORAGE_URL")
AWS_S3_CUSTOM_DOMAIN = env.str("STORAGE_PATH")
AWS_S3_URL_PROTOCOL = env.str("STORAGE_PROTOCOL", "https:")
AWS_S3_FILE_OVERWRITE = False
default_storage = Storage(env.str("STORAGE_DEFAULT"), "default")
static_storage = Storage(env.str("STORAGE_STATIC"), "static")
STORAGES = {
"default": {
"BACKEND": default_storage.get_backend(),
"OPTIONS": default_storage.get_options(),
},
"staticfiles": {
"BACKEND": static_storage.get_backend(),
"OPTIONS": static_storage.get_options(),
},
}

View File

@@ -0,0 +1,41 @@
from . import navigation
UNFOLD = {
"DASHBOARD_CALLBACK": "django_core.views.dashboard_callback",
"SITE_TITLE": None,
"SHOW_LANGUAGES": True,
"SITE_HEADER": None,
"SITE_URL": "/",
"SITE_SYMBOL": "speed", # symbol from icon set
"SHOW_HISTORY": True, # show/hide "History" button, default: True
"SHOW_VIEW_ON_SITE": True,
"COLORS": {
"primary": {
"50": "220 255 230",
"100": "190 255 200",
"200": "160 255 170",
"300": "130 255 140",
"400": "100 255 110",
"500": "70 255 80",
"600": "50 225 70",
"700": "40 195 60",
"800": "30 165 50",
"900": "20 135 40",
"950": "10 105 30",
},
},
"EXTENSIONS": {
"modeltranslation": {
"flags": {
"en": "🇬🇧",
"uz": "🇺🇿",
"ru": "🇷🇺",
},
},
},
"SIDEBAR": {
"show_search": True, # Search in applications and models names
"show_all_applications": True,
# "navigation": navigation.PAGES, # Pagelarni qo'lda qo'shish
},
}

View File

@@ -0,0 +1,28 @@
"""
Default value for environ variable
"""
import os
import environ
environ.Env.read_env(os.path.join(".env"))
env = environ.Env(
DEBUG=(bool, False),
CACHE_TIME=(int, 180),
OTP_EXPIRE_TIME=(int, 2),
VITE_LIVE=(bool, False),
ALLOWED_HOSTS=(str, "localhost"),
CSRF_TRUSTED_ORIGINS=(str, "localhost"),
DJANGO_SETTINGS_MODULE=(str, "config.settings.local"),
CACHE_TIMEOUT=(int, 120),
CACHE_ENABLED=(bool, False),
VITE_PORT=(int, 5173),
VITE_HOST=(str, "vite"),
NGROK_AUTHTOKEN=(str, "TOKEN"),
BOT_TOKEN=(str, "TOKEN"),
OTP_MODULE="core.services.otp",
OTP_SERVICE="EskizService",
PROJECT_ENV=(str, "prod"),
)

View File

@@ -0,0 +1,169 @@
import os
import pathlib
from typing import List, Union
from config.conf import * # noqa
from config.conf.apps import APPS
from config.conf.modules import MODULES
from config.env import env
from django.utils.translation import gettext_lazy as _
from rich.traceback import install
install(show_locals=True)
BASE_DIR = pathlib.Path(__file__).resolve().parent.parent.parent
SECRET_KEY = env.str("DJANGO_SECRET_KEY")
DEBUG = env.bool("DEBUG")
ALLOWED_HOSTS: Union[List[str]] = ["*"]
if env.bool("PROTOCOL_HTTPS", False):
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
DATABASES = {
"default": {
"ENGINE": env.str("DB_ENGINE"),
"NAME": env.str("DB_NAME"),
"USER": env.str("DB_USER"),
"PASSWORD": env.str("DB_PASSWORD"),
"HOST": env.str("DB_HOST"),
"PORT": env.str("DB_PORT"),
}
}
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.BCryptPasswordHasher",
]
INSTALLED_APPS = [
"unfold",
"unfold.contrib.filters",
"unfold.contrib.forms",
"unfold.contrib.guardian",
"unfold.contrib.simple_history",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
] + APPS
MODULES = [app for app in MODULES if isinstance(app, str)]
for module_path in MODULES:
INSTALLED_APPS.append("{}.apps.ModuleConfig".format(module_path))
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware", # Cors middleware
"django.middleware.locale.LocaleMiddleware", # Locale middleware
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
if env.str("PROJECT_ENV") == "debug":
MIDDLEWARE += [
"silk.middleware.SilkyMiddleware",
]
ROOT_URLCONF = "config.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "resources/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",
],
},
},
]
# fmt: off
WSGI_APPLICATION = "config.wsgi.application"
# fmt: on
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.{}".format(validator)
} for validator in [
"UserAttributeSimilarityValidator",
"MinimumLengthValidator",
"CommonPasswordValidator",
"NumericPasswordValidator"
]
]
TIME_ZONE = "Asia/Tashkent"
USE_I18N = True
USE_TZ = True
STATIC_URL = "/auth/resources/static/"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# Date formats
##
DATE_FORMAT = "d.m.y"
TIME_FORMAT = "H:i:s"
DATE_INPUT_FORMATS = ["%d.%m.%Y", "%Y.%d.%m", "%Y.%d.%m"]
SEEDERS = ["core.apps.accounts.seeder.UserSeeder"]
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "resources/static"),
]
CORS_ORIGIN_ALLOW_ALL = True
STATIC_ROOT = os.path.join(BASE_DIR, "resources/staticfiles")
VITE_APP_DIR = os.path.join(BASE_DIR, "resources/static/vite")
LANGUAGES = (
("ru", _("Russia")),
("en", _("English")),
("uz", _("Uzbek")),
)
LOCALE_PATHS = [os.path.join(BASE_DIR, "resources/locale")]
LANGUAGE_CODE = "uz"
MEDIA_ROOT = os.path.join(BASE_DIR, "resources/media") # Media files
MEDIA_URL = "/auth/resources/media/"
AUTH_USER_MODEL = "accounts.User"
CELERY_BROKER_URL = env("REDIS_URL")
CELERY_RESULT_BACKEND = env("REDIS_URL")
ALLOWED_HOSTS += env("ALLOWED_HOSTS").split(",")
CSRF_TRUSTED_ORIGINS = env("CSRF_TRUSTED_ORIGINS").split(",")
SILKY_AUTHORISATION = True
SILKY_PYTHON_PROFILER = True
JST_LANGUAGES = [
{
"code": "uz",
"name": "Uzbek",
"is_default": True,
},
{
"code": "en",
"name": "English",
},
{
"code": "ru",
"name": "Russia",
}
]

View File

@@ -0,0 +1,11 @@
from config.settings.common import * # noqa
from config.settings.common import (ALLOWED_HOSTS, INSTALLED_APPS,
REST_FRAMEWORK)
INSTALLED_APPS += ["django_extensions"]
ALLOWED_HOSTS += ["127.0.0.1", "192.168.100.26"]
REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"] = {
"user": "60/min",
}

View File

@@ -0,0 +1,6 @@
from config.settings.common import * # noqa
from config.settings.common import ALLOWED_HOSTS, REST_FRAMEWORK
ALLOWED_HOSTS += ["192.168.100.26", "80.90.178.156"]
REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"] = {"user": "60/min"}

View File

@@ -0,0 +1,55 @@
"""
All urls configurations tree
"""
from django.conf import settings
from config.env import env
from django.contrib import admin
from django.urls import include, path, re_path
from django.views.static import serve
from drf_spectacular.views import (SpectacularAPIView, SpectacularRedocView,
SpectacularSwaggerView)
################
# My apps url
################
urlpatterns = [
path("", include("core.apps.accounts.urls")),
]
################
# Library urls
################
urlpatterns += [
path("admin/", admin.site.urls),
path("accounts/", include("django.contrib.auth.urls")),
path("i18n/", include("django.conf.urls.i18n")),
]
################
# Project env debug mode
################
if env.str("PROJECT_ENV") == "debug":
urlpatterns += [
path('silk/', include('silk.urls', namespace='silk'))
]
################
# Swagger urls
################
urlpatterns += [
path("schema/", SpectacularAPIView.as_view(), name="schema"),
path("swagger/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"),
path("redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"),
]
################
# Media urls
################
urlpatterns += [
re_path(r"static/(?P<path>.*)", serve, {"document_root": settings.STATIC_ROOT}),
re_path(r"media/(?P<path>.*)", serve, {"document_root": settings.MEDIA_ROOT}),
]

View File

@@ -0,0 +1,8 @@
import os
from config.env import env
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", env("DJANGO_SETTINGS_MODULE"))
application = get_wsgi_application()