gealogiuya
Some checks failed
Deploy Django Application to Server / deploy (push) Failing after 19s
Telegram Notifications / Telegram Gate (push) Failing after 5s

This commit is contained in:
2026-02-27 14:56:23 +05:00
commit 0229a0595c
118 changed files with 33948 additions and 0 deletions

3
core/__init__.py Normal file
View File

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

16
core/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for core project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
application = get_asgi_application()

14
core/celery.py Normal file
View File

@@ -0,0 +1,14 @@
import os
from celery import Celery
# from celery.schedules import crontab
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
app = Celery("core")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()

9
core/config/__init__.py Normal file
View File

@@ -0,0 +1,9 @@
from .apps import * # noqa
# from .cache import * # noqa
# from .ckeditor5 import * # noqa
# from .jazzmin import * # noqa
from .jwt import * # noqa
from .rest_framework import * # noqa
from .unfold import * # noqa
from .unfold_navigation import * # noqa

30
core/config/apps.py Normal file
View File

@@ -0,0 +1,30 @@
THIRD_PARTY_APPS = [
# "jazzmin",
"unfold",
"unfold.contrib.filters",
"unfold.contrib.forms",
"unfold.contrib.import_export",
"unfold.contrib.guardian",
"unfold.contrib.simple_history",
"modeltranslation",
"django_ckeditor_5",
"corsheaders",
"rosetta",
"rest_framework",
"drf_spectacular",
"drf_spectacular_sidecar",
]
DEFAULT_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
PROJECT_APPS = [
"apps.shared.apps.SharedConfig",
"apps.geology.apps.GeologyConfig",
]

17
core/config/cache.py Normal file
View File

@@ -0,0 +1,17 @@
import os
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": os.getenv("REDIS_CACHE_URL"),
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
"KEY_PREFIX": "core",
}
}
CACHE_MIDDLEWARE_SECONDS = os.getenv("CACHE_TIMEOUT")
# In your Django settings.py or a dedicated Celery configuration module
CELERY_BROKER_URL = os.getenv("CELERY_BROKER", "redis://redis:6379/0")
CELERY_RESULT_BACKEND = os.getenv("RABBITMQ_RESULT_BACKEND")

140
core/config/ckeditor5.py Normal file
View File

@@ -0,0 +1,140 @@
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",
}
},
}

69
core/config/jazzmin.py Normal file
View File

@@ -0,0 +1,69 @@
JAZZMIN_SETTINGS = {
"site_title": "Django Admin",
"site_header": "Django Admin",
"site_brand": "Django Admin",
"welcome_sign": "Django Admin",
"site_title_short": "Django Admin",
"login_logo": "image/logo.png",
"site_icon": "image/logo.png",
"site_logo": "image/logo.png",
"login_logo_dark": "image/logo.png",
"user_avatar": None,
"show_sidebar": True,
"navigation_expanded": True,
"icons": {
"auth": "fas fa-users-cog",
"auth.user": "fas fa-user",
"auth.Group": "fas fa-users",
},
"topmenu_links": [
{"name": "Home", "url": "/", "new_window": False},
],
"copyright": "University Admin",
"default_icon_parents": "fas fa-chevron-circle-right",
"default_icon_children": "fas fa-circle",
"related_modal_active": False,
"custom_js": None,
"custom_css": "css/jazzmin.css",
"use_google_fonts_cdn": True,
"changeform_format": "horizontal_tabs",
"changeform_format_overrides": {
"auth.user": "collapsible",
"auth.group": "vertical_tabs",
},
"site_logo_classes": "img-circle",
"language_chooser": True,
}
JAZZMIN_UI_TWEAKS = {
"navbar_small_text": False,
"footer_small_text": False,
"body_small_text": True,
"brand_small_text": False,
"brand_colour": "navbar-navy",
"accent": "accent-primary",
"navbar": "navbar-navy navbar-dark",
"no_navbar_border": False,
"navbar_fixed": False,
"layout_boxed": False,
"footer_fixed": False,
"sidebar_fixed": True,
"sidebar": "sidebar-dark-navy",
"sidebar_nav_small_text": False,
"sidebar_disable_expand": False,
"sidebar_nav_child_indent": False,
"sidebar_nav_compact_style": True,
"sidebar_nav_legacy_style": False,
"sidebar_nav_flat_style": False,
"theme": "minty",
"dark_mode_theme": None,
"button_classes": {
"primary": "btn-outline-primary",
"secondary": "btn-outline-secondary",
"info": "btn-outline-info",
"warning": "btn-warning",
"danger": "btn-danger",
"success": "btn-success",
},
"actions_sticky_top": False,
}

36
core/config/jwt.py Normal file
View File

@@ -0,0 +1,36 @@
import os
from datetime import timedelta
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(days=1),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
"ROTATE_REFRESH_TOKENS": False,
"BLACKLIST_AFTER_ROTATION": False,
"UPDATE_LAST_LOGIN": False,
"ALGORITHM": "HS256",
"SIGNING_KEY": os.getenv("SECRET_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",
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
"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=1),
"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,20 @@
REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
"DEFAULT_THROTTLE_RATE": {
"anon": "1000/day",
"user": "1000/day",
"user_create": "1000/day",
},
}
SPECTACULAR_SETTINGS = {
"SWAGGER_UI_DIST": "SIDECAR",
"SWAGGER_UI_FAVICON_HREF": "SIDECAR",
"REDOC_DIST": "SIDECAR",
"TITLE": "Django Rest API",
"DESCRIPTION": "Django Rest API",
}

29
core/config/swagger.py Normal file
View File

@@ -0,0 +1,29 @@
from django.contrib.auth.decorators import login_required
from django.urls import path
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularRedocView,
SpectacularSwaggerView,
)
urlpatterns = [
path(
"schema/",
login_required(SpectacularAPIView.as_view(), login_url="/admin/"),
name="schema",
),
path(
"api/schema/swagger-ui/",
login_required(
SpectacularSwaggerView.as_view(url_name="schema"), login_url="/admin/"
),
name="swagger-ui",
),
path(
"api/schema/redoc/",
login_required(
SpectacularRedocView.as_view(url_name="schema"), login_url="/admin/"
),
name="redoc",
),
]

107
core/config/unfold.py Normal file
View File

@@ -0,0 +1,107 @@
from django.templatetags.static import static
from django.utils.translation import gettext_lazy as _
from django.urls import reverse_lazy
from . import unfold_navigation as navigation
UNFOLD = {
"SITE_TITLE": "Django Default",
"SITE_HEADER": "Django Default",
"SITE_URL": "/",
"SITE_ICON": {
"light": lambda request: static("images/django-logo.png"),
"dark": lambda request: static("images/django-logo.png"),
},
"SITE_FAVICONS": [
{
"rel": "icon",
"sizes": "32x32",
"type": "image/svg+xml",
"href": lambda request: static("images/django-logo.png"),
},
],
"SITE_SYMBOL": "speed",
"SHOW_HISTORY": True,
"SHOW_VIEW_ON_SITE": True,
"LOGIN": {
"image": lambda request: static("images/login.jpg"),
},
"STYLES": [
lambda request: static("css/tailwind.css"),
],
"COLORS": {
"font": {
"subtle-light": "107 114 128",
"subtle-dark": "156 163 175",
"default-light": "75 85 99",
"default-dark": "209 213 219",
"important-light": "17 24 39",
"important-dark": "243 244 246",
},
"primary": {
"50": "65 144 176",
"100": "65 144 176",
"200": "65 144 176",
"300": "65 144 176",
"400": "65 144 176",
"500": "65 144 176",
"600": "65 144 176",
"700": "65 144 176",
"800": "65 144 176",
"900": "65 144 176",
"950": "65 144 176",
},
},
"EXTENSIONS": {
"modeltranslation": {
"flags": {
"uz": "🇺🇿",
"ru": "🇷🇺",
},
},
},
"SIDEBAR": {
"show_search": True,
"show_all_applications": True,
"navigation": navigation.PAGES,
},
"TABS": [
{
"models": [
"geology.geology",
"geology.photo",
"geology.category",
],
"items": [
{
"title": _("Madanlar"),
"link": reverse_lazy("admin:geology_geology_changelist"),
},
{
"title": _("Fotolar"),
"link": reverse_lazy("admin:geology_photo_changelist"),
},
{
"title": _("Kategoriyalar"),
"link": reverse_lazy("admin:geology_category_changelist"),
},
],
},
{
"models": [
"geology.gallerycategory",
"geology.gallery",
],
"items": [
{
"title": _("Galereya kategoriyalari"),
"link": reverse_lazy("admin:geology_gallerycategory_changelist"),
},
{
"title": _("Galereya"),
"link": reverse_lazy("admin:geology_gallery_changelist"),
},
],
},
],
}

View File

@@ -0,0 +1,71 @@
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
def user_has_group_or_permission(user, permission):
if user.is_superuser:
return True
group_names = user.groups.values_list("name", flat=True)
if not group_names:
return True
return user.groups.filter(permissions__codename=permission).exists()
PAGES = [
{
"seperator": True,
"items": [
{
"title": _("Bosh sahifa"),
"icon": "home",
"link": reverse_lazy("admin:index"),
},
],
},
{
"seperator": True,
"title": _("Foydalanuvchilar"),
"items": [
{
"title": _("Guruhlar"),
"icon": "person_add",
"link": reverse_lazy("admin:auth_group_changelist"),
"permission": lambda request: user_has_group_or_permission(
request.user, "view_group"
),
},
{
"title": _("Foydalanuvchilar"),
"icon": "person_add",
"link": reverse_lazy("admin:auth_user_changelist"),
"permission": lambda request: user_has_group_or_permission(
request.user, "view_user"
),
},
],
},
{
"seperator": True,
"title": _("Geologiya"),
"items": [
{
"title": _("Madanlar"),
"icon": "hiking",
"link": reverse_lazy("admin:geology_geology_changelist"),
"permission": lambda request: user_has_group_or_permission(
request.user, "view_geology"
),
},
{
"title": _("Galereya"),
"icon": "gallery_thumbnail",
"link": reverse_lazy("admin:geology_gallerycategory_changelist"),
"permission": lambda request: user_has_group_or_permission(
request.user, "view_photo"
),
},
],
},
]

130
core/settings.py Normal file
View File

@@ -0,0 +1,130 @@
import os # noqa
from pathlib import Path
from django.utils.translation import gettext_lazy as _
from dotenv import load_dotenv, find_dotenv
from core.config import * # noqa
load_dotenv(find_dotenv(".env"))
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.getenv("SECRET_KEY")
DEBUG = os.getenv("DEBUG")
if DEBUG is not None:
DEBUG = DEBUG.lower() in ["true", "1"]
else:
DEBUG = False
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS").split(",")
CSRF_TRUSTED_ORIGINS = os.getenv("CSRF_TRUSTED_ORIGINS").split(",")
INSTALLED_APPS = THIRD_PARTY_APPS + DEFAULT_APPS + PROJECT_APPS # noqa
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.BrokenLinkEmailsMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "core.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "assets/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 = "core.wsgi.application"
ASGI_APPLICATION = "core.asgi.application"
# DATABASES = {
# "default": {
# "ENGINE": "django.db.backends.sqlite3",
# "NAME": BASE_DIR / "db.sqlite3",
# }
# }
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.getenv("POSTGRES_DB"),
"USER": os.getenv("POSTGRES_USER"),
"PASSWORD": os.getenv("POSTGRES_PASSWORD"),
"HOST": os.getenv("POSTGRES_HOST"),
"PORT": os.getenv("POSTGRES_PORT"),
}
}
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",
},
]
LANGUAGE_CODE = "uz"
TIME_ZONE = "Asia/Tashkent"
USE_I18N = True
USE_TZ = True
LANGUAGES = (
("ru", _("Russia")),
("en", _("English")),
("uz", _("Uzbek")),
)
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
MODELTRANSLATION_LANGUAGES = ("uz", "ru", "en")
MODELTRANSLATION_DEFAULT_LANGUAGE = "uz"
STATIC_URL = "static/"
STATICFILES_DIRS = [str(BASE_DIR.joinpath("assets/static"))]
STATIC_ROOT = str(BASE_DIR.joinpath("assets/staticfiles"))
MEDIA_URL = "media/"
MEDIA_ROOT = str(BASE_DIR.joinpath("assets/media"))
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
CORS_ALLOW_ALL_ORIGINS = True
# Security settings
SECURE_HSTS_SECONDS = 3600
SECURE_SSL_REDIRECT = False
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

21
core/urls.py Normal file
View File

@@ -0,0 +1,21 @@
from django.conf import settings
from django.conf.urls.i18n import i18n_patterns # noqa: F401
from django.contrib import admin
from django.urls import path, include, re_path
from django.views.static import serve
from core.config.swagger import urlpatterns as swagger_patterns
urlpatterns = [
path("", include("apps.shared.urls")),
path("", include("apps.geology.urls")),
path("admin/", admin.site.urls),
path("i18n/", include("django.conf.urls.i18n")),
path("ckeditor5/", include("django_ckeditor_5.urls")),
path("rosetta/", include("rosetta.urls")),
# Media and static files
re_path(r"static/(?P<path>.*)", serve, {"document_root": settings.STATIC_ROOT}),
re_path(r"media/(?P<path>.*)", serve, {"document_root": settings.MEDIA_ROOT}),
]
urlpatterns += swagger_patterns

16
core/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for core project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
application = get_wsgi_application()