You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
734 lines
28 KiB
734 lines
28 KiB
"""
|
|
Django base settings for project template.
|
|
Built on Django 5.0+, Django REST Framework, Celery, and Django Unfold.
|
|
"""
|
|
import os
|
|
from pathlib import Path
|
|
from django.templatetags.static import static
|
|
from django.urls import reverse_lazy
|
|
import environ
|
|
from django.utils.translation import gettext_lazy as _
|
|
import sentry_sdk
|
|
from corsheaders.defaults import default_headers
|
|
|
|
# Build paths inside the project: BASE_DIR / 'subdir'.
|
|
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
|
|
|
env = environ.Env()
|
|
env_file = os.path.join(BASE_DIR, '.env')
|
|
if os.path.exists(env_file):
|
|
environ.Env.read_env(env_file)
|
|
|
|
# Core Security & Debug
|
|
SECRET_KEY = env('DJANGO_SECRET_KEY', default='django-insecure-template-secret-key-replace-in-production')
|
|
DEBUG = env.bool('DJANGO_DEBUG', default=True)
|
|
|
|
ALLOWED_HOSTS = [host.strip() for host in env('DJANGO_ALLOWED_HOSTS', default='127.0.0.1,localhost,*').split(',') if host.strip()]
|
|
CSRF_TRUSTED_ORIGINS = [origin.strip() for origin in env('DJANGO_TRUSTED_ORIGINS', default='http://localhost:8000,http://127.0.0.1:8000,http://localhost:8080,http://127.0.0.1:8080,http://localhost:5173,http://127.0.0.1:5173,http://localhost:3000,http://127.0.0.1:3000').split(',') if origin.strip()]
|
|
|
|
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
|
|
|
# Application definition
|
|
LOCAL_APPS = [
|
|
'apps.account.apps.AccountConfig',
|
|
'apps.profiles.apps.ProfilesConfig',
|
|
'apps.geo_map.apps.GeoMapConfig',
|
|
'apps.cms.apps.CMSConfig',
|
|
'apps.projects.apps.ProjectsConfig',
|
|
'apps.chat.apps.ChatConfig',
|
|
'apps.events.apps.EventsConfig',
|
|
'apps.meetings.apps.MeetingsConfig',
|
|
'apps.lms.apps.LMSConfig',
|
|
'apps.tickets.apps.TicketsConfig',
|
|
'apps.dynamic_forms.apps.DynamicFormsConfig',
|
|
'apps.donations.apps.DonationsConfig',
|
|
'apps.diplomacy.apps.DiplomacyConfig',
|
|
'apps.analytics.apps.AnalyticsConfig',
|
|
'apps.search.apps.SearchConfig',
|
|
'apps.api.apps.ApiConfig',
|
|
'dynamic_preferences',
|
|
]
|
|
|
|
THIRD_PARTY_APPS = [
|
|
'rest_framework',
|
|
'rest_framework.authtoken',
|
|
'rest_framework_simplejwt',
|
|
'drf_spectacular',
|
|
'drf_spectacular_sidecar',
|
|
'phonenumber_field',
|
|
'corsheaders',
|
|
'django_filters',
|
|
'django_celery_beat',
|
|
]
|
|
|
|
INSTALLED_APPS = [
|
|
"unfold",
|
|
"unfold.contrib.filters",
|
|
"unfold.contrib.forms",
|
|
"unfold.contrib.inlines",
|
|
"whitenoise.runserver_nostatic",
|
|
'django.contrib.admin',
|
|
'django.contrib.auth',
|
|
'django.contrib.contenttypes',
|
|
'django.contrib.sessions',
|
|
'django.contrib.messages',
|
|
'django.contrib.staticfiles',
|
|
'django.contrib.humanize',
|
|
'django.contrib.sitemaps',
|
|
*THIRD_PARTY_APPS,
|
|
*LOCAL_APPS,
|
|
]
|
|
|
|
AUTHENTICATION_BACKENDS = [
|
|
'django.contrib.auth.backends.ModelBackend',
|
|
'apps.account.custom_user_login.CustomLoginBackend',
|
|
]
|
|
|
|
AUTH_USER_MODEL = "account.User"
|
|
|
|
MIDDLEWARE = [
|
|
'corsheaders.middleware.CorsMiddleware',
|
|
'django.middleware.security.SecurityMiddleware',
|
|
'django.middleware.gzip.GZipMiddleware',
|
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
'django.middleware.locale.LocaleMiddleware',
|
|
'django.middleware.common.CommonMiddleware',
|
|
'django.middleware.csrf.CsrfViewMiddleware',
|
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
'django.contrib.messages.middleware.MessageMiddleware',
|
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
'config.language_code_middleware.language_middleware',
|
|
'config.enhanced_auth_middleware.enhanced_auth_middleware',
|
|
]
|
|
|
|
ROOT_URLCONF = 'config.urls'
|
|
APPEND_SLASH = True
|
|
|
|
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',
|
|
'django.template.context_processors.i18n',
|
|
"utils.admin.variables",
|
|
],
|
|
},
|
|
},
|
|
]
|
|
|
|
WSGI_APPLICATION = 'config.wsgi.application'
|
|
ASGI_APPLICATION = 'config.asgi.application'
|
|
|
|
# Database configuration
|
|
DATABASES = {
|
|
'default': {
|
|
'ENGINE': 'django.db.backends.postgresql',
|
|
'NAME': env('POSTGRES_DB', default='app_db'),
|
|
'USER': env('POSTGRES_USER', default='postgres'),
|
|
'PASSWORD': env('POSTGRES_PASSWORD', default='postgres'),
|
|
'HOST': env('POSTGRES_HOST', default='postgres'),
|
|
'PORT': env('POSTGRES_PORT', default='5432'),
|
|
'ATOMIC_REQUESTS': True,
|
|
},
|
|
}
|
|
|
|
# Redis & Caching
|
|
REDIS_URL = env('REDIS_URL', default='redis://redis:6379/0')
|
|
CACHES = {
|
|
'default': {
|
|
"BACKEND": "django_redis.cache.RedisCache",
|
|
"LOCATION": REDIS_URL,
|
|
"OPTIONS": {
|
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
}
|
|
}
|
|
}
|
|
|
|
# Celery Task Queue
|
|
CELERY_BROKER_URL = REDIS_URL
|
|
CELERY_RESULT_BACKEND = REDIS_URL
|
|
CELERY_ACCEPT_CONTENT = ['application/json']
|
|
CELERY_TIMEZONE = 'UTC'
|
|
CELERY_BROKER_TRANSPORT = 'redis'
|
|
CELERY_TASK_SERIALIZER = 'json'
|
|
CELERY_RESULT_SERIALIZER = 'json'
|
|
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
|
|
|
|
from celery.schedules import crontab
|
|
CELERY_BEAT_SCHEDULE = {
|
|
'cleanup_expired_tokens_daily': {
|
|
'task': 'cleanup_expired_tokens_task',
|
|
'schedule': crontab(minute=0, hour=3),
|
|
},
|
|
}
|
|
|
|
# Password validation
|
|
AUTH_PASSWORD_VALIDATORS = [
|
|
{
|
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
|
'OPTIONS': {
|
|
'min_length': 6,
|
|
}
|
|
},
|
|
]
|
|
|
|
# Django REST Framework
|
|
REST_FRAMEWORK = {
|
|
'DEFAULT_PAGINATION_CLASS': 'utils.pagination.StandardResultsSetPagination',
|
|
'PAGE_SIZE': 20,
|
|
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
|
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
|
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
|
'rest_framework.authentication.TokenAuthentication',
|
|
'rest_framework.authentication.SessionAuthentication',
|
|
],
|
|
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
|
'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler',
|
|
}
|
|
|
|
# SimpleJWT Authentication Settings
|
|
from datetime import timedelta
|
|
SIMPLE_JWT = {
|
|
'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
|
|
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
|
|
'ROTATE_REFRESH_TOKENS': True,
|
|
'BLACKLIST_AFTER_ROTATION': False,
|
|
'UPDATE_LAST_LOGIN': True,
|
|
'ALGORITHM': 'HS256',
|
|
'SIGNING_KEY': SECRET_KEY,
|
|
'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',),
|
|
}
|
|
|
|
# drf-spectacular OpenAPI 3.0 Documentation Settings
|
|
SPECTACULAR_SETTINGS = {
|
|
'TITLE': 'API سامانه تعاملات بینالملل',
|
|
'DESCRIPTION': 'مستندات نقاط پایانی سامانه تعاملات بینالملل آستان قدس رضوی (فاز دوم)',
|
|
'VERSION': '2.0.0',
|
|
'SERVE_INCLUDE_SCHEMA': False,
|
|
'COMPONENT_SPLIT_REQUEST': True,
|
|
'SWAGGER_UI_DIST': 'SIDECAR',
|
|
'SWAGGER_UI_FAVICON_HREF': 'SIDECAR',
|
|
'REDOC_DIST': 'SIDECAR',
|
|
'SECURITY': [{
|
|
'jwtAuth': []
|
|
}],
|
|
'SECURITY_SCHEMES': {
|
|
'jwtAuth': {
|
|
'type': 'apiKey',
|
|
'in': 'header',
|
|
'name': 'Authorization',
|
|
'description': 'توکن JWT را به این صورت وارد کنید: Bearer <JWT_TOKEN>'
|
|
}
|
|
},
|
|
'SWAGGER_UI_SETTINGS': {
|
|
'deepLinking': True,
|
|
'persistAuthorization': True,
|
|
'displayOperationId': True,
|
|
},
|
|
'ENUM_NAME_OVERRIDES': {
|
|
'VerificationDocumentStatusEnum': 'apps.account.models.verification.VerificationDocument.Status',
|
|
'CMSPostStatusEnum': 'apps.cms.models.post.Post.Status',
|
|
'CMSCommentStatusEnum': 'apps.cms.models.post.PostComment.Status',
|
|
'ProjectStatusEnum': 'apps.projects.models.project.Project.Status',
|
|
'MeetingStatusEnum': 'apps.meetings.models.meeting.MeetingStatus',
|
|
'EventRegistrationStatusEnum': 'apps.events.models.event.RegistrationStatus',
|
|
'EventCategoryEnum': 'apps.events.models.event.EventCategory',
|
|
'LMSCourseCategoryEnum': 'apps.lms.models.course.CourseCategory',
|
|
'LMSCourseLevelEnum': 'apps.lms.models.course.CourseLevel',
|
|
'ContentLanguageEnum': 'apps.lms.models.course.CourseLanguage',
|
|
'LMSLessonTypeEnum': 'apps.lms.models.course.Lesson.LessonType',
|
|
'TicketCategoryEnum': 'apps.tickets.models.ticket.TicketCategory',
|
|
'TicketPriorityEnum': 'apps.tickets.models.ticket.TicketPriority',
|
|
'TicketStatusEnum': 'apps.tickets.models.ticket.TicketStatus',
|
|
'WorkflowStatusEnum': 'apps.dynamic_forms.models.form.WorkflowStatus',
|
|
'DonationCampaignCategoryEnum': 'apps.donations.models.donation.CampaignCategory',
|
|
'DonationCampaignPriorityEnum': 'apps.donations.models.donation.CampaignPriority',
|
|
'PaymentStatusEnum': 'apps.donations.models.donation.PaymentStatus',
|
|
'DiplomacyReportStatusEnum': 'apps.diplomacy.models.report.ReportStatus',
|
|
'InstitutionTypeEnum': 'apps.profiles.models.institution.Institution.InstitutionType',
|
|
'SearchEntityTypeEnum': 'apps.search.serializers.SearchEntityType',
|
|
},
|
|
}
|
|
|
|
# Centrifugo Real-Time Messaging Settings
|
|
CENTRIFUGO_API_URL = env('CENTRIFUGO_API_URL', default='http://centrifugo:8000/api')
|
|
CENTRIFUGO_SECRET = env('CENTRIFUGO_SECRET', default='super_secret_centrifugo_token_key_hmac_256')
|
|
CENTRIFUGO_API_KEY = env('CENTRIFUGO_API_KEY', default='centrifugo_internal_api_access_key')
|
|
|
|
CORS_ALLOW_ALL_ORIGINS = True
|
|
CORS_ALLOW_CREDENTIALS = True
|
|
CORS_ALLOWED_ORIGINS = [
|
|
'http://localhost:8080',
|
|
'http://127.0.0.1:8080',
|
|
'http://localhost:5173',
|
|
'http://127.0.0.1:5173',
|
|
'http://localhost:3000',
|
|
'http://127.0.0.1:3000',
|
|
'https://shiadeepconnect.org',
|
|
]
|
|
CORS_ALLOW_HEADERS = list(default_headers) + [
|
|
'accept-language',
|
|
'content-disposition',
|
|
'x-csrftoken',
|
|
'x-requested-with',
|
|
]
|
|
|
|
# Internationalization
|
|
LANGUAGE_CODE = 'en'
|
|
TIME_ZONE = 'UTC'
|
|
USE_I18N = True
|
|
USE_L10N = True
|
|
USE_TZ = True
|
|
|
|
LANGUAGES = [
|
|
('fa', _('Persian')),
|
|
('en', _('English')),
|
|
('ar', _('Arabic')),
|
|
('ur', _('Urdu')),
|
|
('ru', _('Russian')),
|
|
]
|
|
|
|
LOCALE_PATHS = [
|
|
os.path.join(BASE_DIR, 'locale'),
|
|
]
|
|
|
|
# Static & Media Files
|
|
STATIC_URL = '/static/'
|
|
MEDIA_URL = '/media/'
|
|
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
|
|
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
|
MEDIA_ROOT = BASE_DIR / 'media'
|
|
|
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
|
|
PHONENUMBER_DEFAULT_REGION = env("PHONENUMBER_DEFAULT_REGION", default="US")
|
|
PHONENUMBER_DB_FORMAT = 'INTERNATIONAL'
|
|
PHONENUMBER_DEFAULT_FORMAT = 'INTERNATIONAL'
|
|
|
|
# Sessions
|
|
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
|
|
LOGIN_URL = "admin:login"
|
|
LOGIN_REDIRECT_URL = "/admin/"
|
|
|
|
|
|
def admin_url_generator(request, url_name):
|
|
from django.urls import reverse
|
|
try:
|
|
return reverse(f"admin:{url_name}")
|
|
except Exception:
|
|
return "#"
|
|
|
|
|
|
# Unfold Admin Interface Settings
|
|
UNFOLD = {
|
|
"SITE_TITLE": _("Admin Portal"),
|
|
"SITE_HEADER": _("Admin Portal"),
|
|
"SITE_SUBHEADER": _("Management Console"),
|
|
"SITE_SYMBOL": "speed",
|
|
"ALLOW_UNICODE_SLUGS": True,
|
|
"SHOW_HISTORY": True,
|
|
"SHOW_LANGUAGES": True,
|
|
"ENVIRONMENT": "utils.environment_callback",
|
|
"DASHBOARD_CALLBACK": "utils.admin.dashboard_callback",
|
|
"SHOW_BACK_BUTTON": True,
|
|
"THEME": "light",
|
|
"STYLES": [
|
|
lambda request: static("css/styles.css"),
|
|
],
|
|
"SCRIPTS": [
|
|
lambda request: static("js/admin_row_click.js"),
|
|
],
|
|
|
|
"TABS": [
|
|
{
|
|
"page": "accounts",
|
|
"models": ["account.user", "account.role", "auth.group"],
|
|
"items": [
|
|
{
|
|
"title": _("Users"),
|
|
"icon": "people",
|
|
"link": lambda request: admin_url_generator(request, "account_user_changelist"),
|
|
},
|
|
{
|
|
"title": _("Roles & Permissions"),
|
|
"icon": "admin_panel_settings",
|
|
"link": lambda request: admin_url_generator(request, "account_role_changelist"),
|
|
},
|
|
{
|
|
"title": _("Groups"),
|
|
"icon": "shield",
|
|
"link": lambda request: admin_url_generator(request, "auth_group_changelist"),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
"SIDEBAR": {
|
|
"show_search": True,
|
|
"show_all_applications": True,
|
|
"navigation": [
|
|
{
|
|
"title": _("Overview"),
|
|
"separator": False,
|
|
"items": [
|
|
{
|
|
"title": _("Dashboard"),
|
|
"icon": "dashboard",
|
|
"link": lambda request: admin_url_generator(request, "index"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("User & Access Management"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("All Users"),
|
|
"icon": "person",
|
|
"link": lambda request: admin_url_generator(request, "account_user_changelist"),
|
|
},
|
|
{
|
|
"title": _("Roles & Permissions"),
|
|
"icon": "admin_panel_settings",
|
|
"link": lambda request: admin_url_generator(request, "account_role_changelist"),
|
|
"permission": lambda request: request.user.can_access_admin_panel(),
|
|
},
|
|
{
|
|
"title": _("Django Groups"),
|
|
"icon": "shield",
|
|
"link": lambda request: admin_url_generator(request, "auth_group_changelist"),
|
|
"permission": lambda request: request.user.is_staff,
|
|
},
|
|
{
|
|
"title": _("Verification Documents"),
|
|
"icon": "verified",
|
|
"link": lambda request: admin_url_generator(request, "account_verificationdocument_changelist"),
|
|
},
|
|
{
|
|
"title": _("Login History"),
|
|
"icon": "history",
|
|
"link": lambda request: admin_url_generator(request, "account_loginhistory_changelist"),
|
|
},
|
|
{
|
|
"title": _("Location History"),
|
|
"icon": "location_on",
|
|
"link": lambda request: admin_url_generator(request, "account_locationhistory_changelist"),
|
|
},
|
|
{
|
|
"title": _("User Notifications"),
|
|
"icon": "notifications",
|
|
"link": lambda request: admin_url_generator(request, "account_notification_changelist"),
|
|
},
|
|
{
|
|
"title": _("Notification Templates"),
|
|
"icon": "mark_email_read",
|
|
"link": lambda request: admin_url_generator(request, "account_notificationtemplate_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Institutions & Profiles"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Institutions"),
|
|
"icon": "account_balance",
|
|
"link": lambda request: admin_url_generator(request, "profiles_institution_changelist"),
|
|
},
|
|
{
|
|
"title": _("Regions"),
|
|
"icon": "public",
|
|
"link": lambda request: admin_url_generator(request, "profiles_region_changelist"),
|
|
},
|
|
{
|
|
"title": _("Institution Members"),
|
|
"icon": "badge",
|
|
"link": lambda request: admin_url_generator(request, "profiles_institutionmember_changelist"),
|
|
},
|
|
{
|
|
"title": _("Historical Timelines"),
|
|
"icon": "timeline",
|
|
"link": lambda request: admin_url_generator(request, "profiles_institutiontimeline_changelist"),
|
|
},
|
|
{
|
|
"title": _("Media Gallery"),
|
|
"icon": "perm_media",
|
|
"link": lambda request: admin_url_generator(request, "profiles_institutionmedia_changelist"),
|
|
},
|
|
{
|
|
"title": _("Followers"),
|
|
"icon": "group_add",
|
|
"link": lambda request: admin_url_generator(request, "profiles_institutionfollower_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Content Management (CMS)"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Posts & Articles"),
|
|
"icon": "article",
|
|
"link": lambda request: admin_url_generator(request, "cms_post_changelist"),
|
|
},
|
|
{
|
|
"title": _("Categories"),
|
|
"icon": "category",
|
|
"link": lambda request: admin_url_generator(request, "cms_postcategory_changelist"),
|
|
},
|
|
{
|
|
"title": _("Tags"),
|
|
"icon": "label",
|
|
"link": lambda request: admin_url_generator(request, "cms_posttag_changelist"),
|
|
},
|
|
{
|
|
"title": _("Comments"),
|
|
"icon": "comment",
|
|
"link": lambda request: admin_url_generator(request, "cms_postcomment_changelist"),
|
|
},
|
|
{
|
|
"title": _("Likes"),
|
|
"icon": "thumb_up",
|
|
"link": lambda request: admin_url_generator(request, "cms_postlike_changelist"),
|
|
},
|
|
{
|
|
"title": _("Media Assets"),
|
|
"icon": "photo_library",
|
|
"link": lambda request: admin_url_generator(request, "cms_mediaasset_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Projects & Kanban"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Projects"),
|
|
"icon": "assignment",
|
|
"link": lambda request: admin_url_generator(request, "projects_project_changelist"),
|
|
},
|
|
{
|
|
"title": _("Kanban Tasks"),
|
|
"icon": "task_alt",
|
|
"link": lambda request: admin_url_generator(request, "projects_kanbantask_changelist"),
|
|
},
|
|
{
|
|
"title": _("Kanban Columns"),
|
|
"icon": "view_column",
|
|
"link": lambda request: admin_url_generator(request, "projects_kanbancolumn_changelist"),
|
|
},
|
|
{
|
|
"title": _("Project Documents"),
|
|
"icon": "description",
|
|
"link": lambda request: admin_url_generator(request, "projects_projectdocument_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Events & Calendar"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Events"),
|
|
"icon": "event",
|
|
"link": lambda request: admin_url_generator(request, "events_event_changelist"),
|
|
},
|
|
{
|
|
"title": _("Event Registrations"),
|
|
"icon": "how_to_reg",
|
|
"link": lambda request: admin_url_generator(request, "events_eventregistration_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Meetings & Scheduling"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Meeting Requests"),
|
|
"icon": "video_camera_front",
|
|
"link": lambda request: admin_url_generator(request, "meetings_meetingrequest_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Learning Management (LMS)"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Courses"),
|
|
"icon": "school",
|
|
"link": lambda request: admin_url_generator(request, "lms_course_changelist"),
|
|
},
|
|
{
|
|
"title": _("Course Modules"),
|
|
"icon": "view_module",
|
|
"link": lambda request: admin_url_generator(request, "lms_coursemodule_changelist"),
|
|
},
|
|
{
|
|
"title": _("Lessons"),
|
|
"icon": "menu_book",
|
|
"link": lambda request: admin_url_generator(request, "lms_lesson_changelist"),
|
|
},
|
|
{
|
|
"title": _("Quiz Questions"),
|
|
"icon": "quiz",
|
|
"link": lambda request: admin_url_generator(request, "lms_quizquestion_changelist"),
|
|
},
|
|
{
|
|
"title": _("Student Enrollments"),
|
|
"icon": "co_present",
|
|
"link": lambda request: admin_url_generator(request, "lms_courseenrollment_changelist"),
|
|
},
|
|
{
|
|
"title": _("Lesson Progress"),
|
|
"icon": "trending_up",
|
|
"link": lambda request: admin_url_generator(request, "lms_lessonprogress_changelist"),
|
|
},
|
|
{
|
|
"title": _("Digital Certificates"),
|
|
"icon": "card_membership",
|
|
"link": lambda request: admin_url_generator(request, "lms_digitalcertificate_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Ticketing & Support"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Support Tickets"),
|
|
"icon": "confirmation_number",
|
|
"link": lambda request: admin_url_generator(request, "tickets_ticket_changelist"),
|
|
},
|
|
{
|
|
"title": _("Ticket Messages"),
|
|
"icon": "forum",
|
|
"link": lambda request: admin_url_generator(request, "tickets_ticketmessage_changelist"),
|
|
},
|
|
{
|
|
"title": _("Support Inquiries"),
|
|
"icon": "contact_support",
|
|
"link": lambda request: admin_url_generator(request, "api_supportmessage_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Dynamic Forms & Surveys"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Form Definitions"),
|
|
"icon": "dynamic_form",
|
|
"link": lambda request: admin_url_generator(request, "dynamic_forms_formdefinition_changelist"),
|
|
},
|
|
{
|
|
"title": _("Form Responses"),
|
|
"icon": "checklist",
|
|
"link": lambda request: admin_url_generator(request, "dynamic_forms_formresponse_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Donations & Financials"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Donation Campaigns"),
|
|
"icon": "volunteer_activism",
|
|
"link": lambda request: admin_url_generator(request, "donations_donationcampaign_changelist"),
|
|
},
|
|
{
|
|
"title": _("Transactions"),
|
|
"icon": "payments",
|
|
"link": lambda request: admin_url_generator(request, "donations_donationtransaction_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Diplomacy & Field Reports"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Representative Reports"),
|
|
"icon": "public",
|
|
"link": lambda request: admin_url_generator(request, "diplomacy_representativereport_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("Real-Time Communication"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Chat Rooms"),
|
|
"icon": "chat",
|
|
"link": lambda request: admin_url_generator(request, "chat_chatroom_changelist"),
|
|
},
|
|
{
|
|
"title": _("Chat Messages"),
|
|
"icon": "message",
|
|
"link": lambda request: admin_url_generator(request, "chat_chatmessage_changelist"),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"title": _("System & Automation"),
|
|
"separator": True,
|
|
"items": [
|
|
{
|
|
"title": _("Global Preferences"),
|
|
"icon": "tune",
|
|
"link": lambda request: admin_url_generator(request, "dynamic_preferences_globalpreferencemodel_changelist"),
|
|
},
|
|
{
|
|
"title": _("App Versions"),
|
|
"icon": "system_update",
|
|
"link": lambda request: admin_url_generator(request, "api_appversion_changelist"),
|
|
},
|
|
{
|
|
"title": _("Celery Periodic Tasks"),
|
|
"icon": "schedule",
|
|
"link": lambda request: admin_url_generator(request, "django_celery_beat_periodictask_changelist"),
|
|
},
|
|
{
|
|
"title": _("Crontab Schedules"),
|
|
"icon": "alarm",
|
|
"link": lambda request: admin_url_generator(request, "django_celery_beat_crontabschedule_changelist"),
|
|
},
|
|
{
|
|
"title": _("Interval Schedules"),
|
|
"icon": "timer",
|
|
"link": lambda request: admin_url_generator(request, "django_celery_beat_intervalschedule_changelist"),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}
|
|
|
|
# Sentry SDK Error Tracking (Configurable via ENV)
|
|
SENTRY_DSN = env('SENTRY_DSN', default='')
|
|
if SENTRY_DSN:
|
|
sentry_sdk.init(
|
|
dsn=SENTRY_DSN,
|
|
send_default_pii=True,
|
|
)
|
|
|
|
# Resend Email Service
|
|
RESEND_API_KEY = env('RESEND_API_KEY', default='')
|
|
RESEND_FROM_EMAIL = env('RESEND_FROM_EMAIL', default='onboarding@resend.dev')
|
|
|
|
|