initial commit
This commit is contained in:
3
config/__init__.py
Normal file
3
config/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from config.services.celery import app as celery_app
|
||||
|
||||
__all__ = ('celery_app',)
|
||||
23
config/api.py
Normal file
23
config/api.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from ninja import Router
|
||||
|
||||
from apps.blog.api.views import blog_router
|
||||
from apps.certificates.api.views import certificates_router
|
||||
from apps.communications.api.views import communications_router
|
||||
from apps.events.api.views import events_router
|
||||
from apps.gallery.api.views import gallery_router
|
||||
from apps.payments.api.views import payments_router
|
||||
from apps.users.api.meta import meta_router
|
||||
from apps.users.api.views import auth_router
|
||||
from core.api.views import health_router
|
||||
|
||||
router = Router()
|
||||
router.add_router("auth/", auth_router, tags=["Authentication"])
|
||||
router.add_router("blog/", blog_router, tags=["Blog"])
|
||||
router.add_router("gallery/", gallery_router, tags=["Gallery"])
|
||||
router.add_router("events/", events_router, tags=["Events"])
|
||||
router.add_router("communications/", communications_router, tags=["Communications"])
|
||||
router.add_router("payments/", payments_router, tags=["Payments"])
|
||||
router.add_router("certificates/", certificates_router, tags=["Certificates"])
|
||||
router.add_router("meta/", meta_router, tags=["Meta"])
|
||||
router.add_router("", health_router, tags=["Health"])
|
||||
|
||||
7
config/asgi.py
Normal file
7
config/asgi.py
Normal file
@@ -0,0 +1,7 @@
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.development')
|
||||
|
||||
application = get_asgi_application()
|
||||
56
config/services/celery.py
Normal file
56
config/services/celery.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""Celery application configuration and scheduling."""
|
||||
|
||||
import os
|
||||
|
||||
from celery import Celery
|
||||
from celery.schedules import crontab
|
||||
from decouple import config
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.development')
|
||||
|
||||
app = Celery('config')
|
||||
app.config_from_object('django.conf:settings', namespace='CELERY')
|
||||
app.autodiscover_tasks()
|
||||
|
||||
app.conf.update(
|
||||
broker_url=config('REDIS_URL', default='redis://localhost:6379/0'),
|
||||
result_backend=config('REDIS_URL', default='redis://localhost:6379/0'),
|
||||
task_serializer='json',
|
||||
accept_content=['json'],
|
||||
result_serializer='json',
|
||||
timezone='UTC',
|
||||
enable_utc=True,
|
||||
task_track_started=True,
|
||||
task_time_limit=30 * 60,
|
||||
task_soft_time_limit=60,
|
||||
worker_prefetch_multiplier=1,
|
||||
worker_max_tasks_per_child=1000,
|
||||
)
|
||||
|
||||
app.conf.beat_schedule = {
|
||||
'send-event-reminders': {
|
||||
'task': 'apps.communications.tasks.send_event_reminders',
|
||||
'schedule': crontab(minute=0, hour='*/1'),
|
||||
'description': 'Runs hourly to notify about upcoming events.',
|
||||
},
|
||||
'send-weekly-newsletter': {
|
||||
'task': 'apps.communications.tasks.send_weekly_newsletter',
|
||||
'schedule': crontab(hour=9, minute=0, day_of_week=1),
|
||||
'description': 'Runs every Monday at 09:00 UTC.',
|
||||
},
|
||||
'cleanup-expired-tokens': {
|
||||
'task': 'apps.communications.tasks.cleanup_expired_tokens',
|
||||
'schedule': crontab(hour=2, minute=0),
|
||||
'description': 'Runs daily at 02:00 UTC.',
|
||||
},
|
||||
'process-scheduled-announcements': {
|
||||
'task': 'apps.communications.tasks.process_scheduled_announcements',
|
||||
'schedule': crontab(minute='*/15'),
|
||||
'description': 'Runs every 15 minutes to dispatch scheduled announcements.',
|
||||
},
|
||||
}
|
||||
|
||||
EMAIL_TIMEOUT_SECONDS = 10
|
||||
|
||||
CELERY_TASK_SOFT_TIME_LIMIT = 20
|
||||
CELERY_TASK_TIME_LIMIT = 30
|
||||
14
config/services/location.py
Normal file
14
config/services/location.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""Configuration for Django location fields backed by OpenStreetMap."""
|
||||
|
||||
DEFAULT_MAP_CENTER = [37.0629098, 50.4232464]
|
||||
|
||||
LOCATION_FIELD = {
|
||||
'map.provider': 'openstreetmap',
|
||||
'map.zoom': 13,
|
||||
'map.center': DEFAULT_MAP_CENTER,
|
||||
'map.language': 'fa',
|
||||
'search.provider': 'nominatim',
|
||||
'search.url': 'https://nominatim.openstreetmap.org/search/',
|
||||
'search.params': {'format': 'json', 'addressdetails': 1},
|
||||
'search.headers': {'User-Agent': 'Django CS Association App'},
|
||||
}
|
||||
12
config/services/notifications.py
Normal file
12
config/services/notifications.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from decouple import config
|
||||
|
||||
# Added VAPID configuration for web push notifications
|
||||
# VAPID Configuration for Web Push Notifications
|
||||
VAPID_PUBLIC_KEY = config('VAPID_PUBLIC_KEY', default='')
|
||||
VAPID_PRIVATE_KEY = config('VAPID_PRIVATE_KEY', default='')
|
||||
VAPID_CLAIMS = {
|
||||
"sub": config('VAPID_SUBJECT', default='mailto:admin@csassociation.com')
|
||||
}
|
||||
|
||||
# Site URL for push notification links
|
||||
SITE_URL = config('SITE_URL', default='http://localhost:8000')
|
||||
94
config/services/unfold.py
Normal file
94
config/services/unfold.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from django.conf import settings
|
||||
from django.templatetags.static import static
|
||||
|
||||
# Django Unfold Configuration
|
||||
UNFOLD = {
|
||||
"SITE_TITLE": "GuilanCE Association Admin",
|
||||
"SITE_HEADER": "GuilanCE Association",
|
||||
"SITE_URL": "/",
|
||||
"SITE_ICON": lambda request: static("img/logo.png"),
|
||||
# "SITE_LOGO": lambda request: static("img/logo.png"),
|
||||
"SITE_SYMBOL": "speed",
|
||||
"SHOW_HISTORY": True,
|
||||
"SHOW_VIEW_ON_SITE": True,
|
||||
# "SHOW_BACK_BUTTON": True,
|
||||
"ENVIRONMENT": "config.services.unfold.environment_callback",
|
||||
"LOGIN": {
|
||||
"image": lambda request: request.build_absolute_uri("/static/images/login-bg.jpg"),
|
||||
"redirect_after": lambda request: request.build_absolute_uri("/admin/"),
|
||||
},
|
||||
"STYLES": [
|
||||
lambda request: request.build_absolute_uri("/static/css/styles.css"),
|
||||
],
|
||||
"SCRIPTS": [
|
||||
lambda request: request.build_absolute_uri("/static/js/scripts.js"),
|
||||
],
|
||||
"COLORS": {
|
||||
"primary": {
|
||||
"50": "250 245 255",
|
||||
"100": "243 232 255",
|
||||
"200": "233 213 255",
|
||||
"300": "216 180 254",
|
||||
"400": "196 144 254",
|
||||
"500": "168 85 247",
|
||||
"600": "147 51 234",
|
||||
"700": "126 34 206",
|
||||
"800": "107 33 168",
|
||||
"900": "88 28 135",
|
||||
},
|
||||
},
|
||||
"EXTENSIONS": {
|
||||
"modeltranslation": {
|
||||
"flags": {
|
||||
"en": "🇺🇸",
|
||||
"fa": "🇮🇷",
|
||||
},
|
||||
},
|
||||
},
|
||||
"SIDEBAR": {
|
||||
"show_search": True,
|
||||
"show_all_applications": True,
|
||||
"navigation": [
|
||||
{
|
||||
"title": "Navigation",
|
||||
"separator": True,
|
||||
"items": [
|
||||
{
|
||||
"title": "Dashboard",
|
||||
"icon": "dashboard",
|
||||
"link": lambda request: request.build_absolute_uri("/admin/"),
|
||||
# "badge": 3
|
||||
},
|
||||
{
|
||||
"title": "Users",
|
||||
"icon": "account_circle",
|
||||
"link": lambda request: request.build_absolute_uri("/admin/users/user/"),
|
||||
},
|
||||
{
|
||||
"title": "Blog",
|
||||
"icon": "post",
|
||||
"link": lambda request: request.build_absolute_uri("/admin/blog/"),
|
||||
},
|
||||
{
|
||||
"title": "Events",
|
||||
"icon": "event",
|
||||
"link": lambda request: request.build_absolute_uri("/admin/events/"),
|
||||
},
|
||||
{
|
||||
"title": "Gallery",
|
||||
"icon": "filter",
|
||||
"link": lambda request: request.build_absolute_uri("/admin/gallery/gallery/"),
|
||||
},
|
||||
{
|
||||
"title": "Communications",
|
||||
"icon": "call",
|
||||
"link": lambda request: request.build_absolute_uri("/admin/communications/"),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def environment_callback(request):
|
||||
return ["Development", "warning"] if settings.DEBUG else ["Production", "success"]
|
||||
10
config/services/zarinpal.py
Normal file
10
config/services/zarinpal.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from decouple import config
|
||||
|
||||
ZARINPAL_MERCHANT_ID = config('ZARINPAL_MERCHANT_ID', default='')
|
||||
ZARINPAL_USE_SANDBOX = config('ZARINPAL_USE_SANDBOX', default=False, cast=bool)
|
||||
|
||||
ZARINPAL_API_BASE = "https://sandbox.zarinpal.com" if ZARINPAL_USE_SANDBOX else "https://payment.zarinpal.com"
|
||||
ZARINPAL_REQUEST_URL = f"{ZARINPAL_API_BASE}/pg/v4/payment/request.json"
|
||||
ZARINPAL_VERIFY_URL = f"{ZARINPAL_API_BASE}/pg/v4/payment/verify.json"
|
||||
ZARINPAL_STARTPAY = f"{ZARINPAL_API_BASE}/pg/StartPay/"
|
||||
ZARINPAL_CALLBACK_URL = config('ZARINPAL_CALLBACK_URL', default='http://localhost:8000/api/payments/callback')
|
||||
240
config/settings/base.py
Normal file
240
config/settings/base.py
Normal file
@@ -0,0 +1,240 @@
|
||||
from decouple import config
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
SECRET_KEY = config('SECRET_KEY')
|
||||
|
||||
DEBUG = config('DEBUG', default=False, cast=bool)
|
||||
|
||||
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='').split(',')
|
||||
|
||||
DJANGO_APPS = [
|
||||
'unfold',
|
||||
'unfold.contrib.filters',
|
||||
'unfold.contrib.forms',
|
||||
'unfold.contrib.import_export',
|
||||
'unfold.contrib.location_field',
|
||||
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
|
||||
THIRD_PARTY_APPS = [
|
||||
'corsheaders',
|
||||
'import_export',
|
||||
'simplemde',
|
||||
'location_field',
|
||||
"django_prometheus",
|
||||
]
|
||||
|
||||
LOCAL_APPS = [
|
||||
"core",
|
||||
"apps.users",
|
||||
"apps.blog",
|
||||
"apps.gallery",
|
||||
"apps.events",
|
||||
"apps.certificates",
|
||||
"apps.communications",
|
||||
"apps.payments",
|
||||
]
|
||||
|
||||
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django_prometheus.middleware.PrometheusBeforeMiddleware",
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||
'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',
|
||||
"django_prometheus.middleware.PrometheusAfterMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'config.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 = 'config.wsgi.application'
|
||||
|
||||
# Database
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': config('DB_ENGINE', 'django.db.backends.sqlite3'),
|
||||
'NAME': config('DB_NAME', BASE_DIR / 'db.sqlite3'),
|
||||
'USER': config('DB_USER'),
|
||||
'PASSWORD': config('DB_PASSWORD'),
|
||||
'HOST': config('DB_HOST', default='localhost'),
|
||||
'PORT': config('DB_PORT', default='5432'),
|
||||
}
|
||||
}
|
||||
|
||||
# Password validation
|
||||
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 = 'en-us'
|
||||
TIME_ZONE = 'Asia/Tehran'
|
||||
|
||||
LANGUAGES = [
|
||||
('en', 'English'),
|
||||
('fa', 'فارسی'),
|
||||
]
|
||||
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
|
||||
# For RTL support in admin
|
||||
LOCALE_PATHS = [BASE_DIR / 'locale']
|
||||
|
||||
STATIC_URL = config('STATIC_URL', default='/static/')
|
||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||
STATICFILES_DIRS = [BASE_DIR / 'static']
|
||||
|
||||
MEDIA_URL = config('MEDIA_URL', default='/media/')
|
||||
MEDIA_ROOT = BASE_DIR / 'media'
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
AUTH_USER_MODEL = 'users.User'
|
||||
|
||||
# CORS / CSRF Settings
|
||||
CORS_ALLOWED_ORIGINS = config(
|
||||
'CORS_ALLOWED_ORIGINS',
|
||||
default='https://east-guilan-ce.ir',
|
||||
).split(',')
|
||||
CORS_ALLOW_CREDENTIALS = config('CORS_ALLOW_CREDENTIALS', default=True, cast=bool)
|
||||
CSRF_TRUSTED_ORIGINS = config(
|
||||
'CSRF_TRUSTED_ORIGINS',
|
||||
default='https://east-guilan-ce.ir',
|
||||
).split(',')
|
||||
CSRF_COOKIE_SECURE = config('CSRF_COOKIE_SECURE', default=True, cast=bool)
|
||||
SESSION_COOKIE_SECURE = config('SESSION_COOKIE_SECURE', default=True, cast=bool)
|
||||
|
||||
# Email Configuration
|
||||
EMAIL_BACKEND = config('EMAIL_BACKEND', default='django.core.mail.backends.console.EmailBackend')
|
||||
EMAIL_HOST = config('EMAIL_HOST', default='')
|
||||
EMAIL_PORT = config('EMAIL_PORT', default=587, cast=int)
|
||||
EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=True, cast=bool)
|
||||
EMAIL_HOST_USER = config('EMAIL_HOST_USER', default='')
|
||||
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default='')
|
||||
DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL', default='webmaster@localhost')
|
||||
|
||||
# JWT Configuration
|
||||
JWT_SECRET_KEY = config('JWT_SECRET_KEY', default=SECRET_KEY)
|
||||
JWT_ALGORITHM = config('JWT_ALGORITHM', default='HS256')
|
||||
JWT_ACCESS_TOKEN_LIFETIME = config('JWT_ACCESS_TOKEN_LIFETIME', default=3600, cast=int)
|
||||
JWT_REFRESH_TOKEN_LIFETIME = config('JWT_REFRESH_TOKEN_LIFETIME', default=86400, cast=int)
|
||||
|
||||
# Redis Configuration
|
||||
REDIS_URL = config('REDIS_URL', default='redis://localhost:6379/0')
|
||||
|
||||
# Cache Configuration
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django_prometheus.cache.backends.redis.RedisCache',
|
||||
'LOCATION': REDIS_URL,
|
||||
}
|
||||
}
|
||||
|
||||
# Celery Configuration
|
||||
CELERY_BROKER_URL = REDIS_URL
|
||||
CELERY_RESULT_BACKEND = REDIS_URL
|
||||
|
||||
|
||||
# Logging Configuration
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
|
||||
'style': '{',
|
||||
},
|
||||
'simple': {
|
||||
'format': '{levelname} {message}',
|
||||
'style': '{',
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'file': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.FileHandler',
|
||||
'filename': BASE_DIR / 'logs' / 'django.log',
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'console': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple',
|
||||
},
|
||||
},
|
||||
'root': {
|
||||
'handlers': ['console'],
|
||||
'level': 'INFO',
|
||||
},
|
||||
'loggers': {
|
||||
'django': {
|
||||
'handlers': ['file', 'console'],
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
},
|
||||
'apps': {
|
||||
'handlers': ['file', 'console'],
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# Create logs directory
|
||||
os.makedirs(BASE_DIR / 'logs', exist_ok=True)
|
||||
|
||||
BACKEND_ROOT = config('DJANGO_HOST', default='http://localhost:8000/')
|
||||
FRONTEND_ROOT = config('FRONTEND_ROOT', default='http://localhost:3000/')
|
||||
FRONTEND_PASSWORD_RESET_PAGE = config('FRONTEND_PASSWORD_RESET_PAGE', default='http://localhost:3000/api/auth/reset-password-confirm/')
|
||||
FRONTEND_CALLBACK_URL = config('FRONTEND_CALLBACK_URL', default='http://localhost:3000/payments/result')
|
||||
|
||||
if DATABASES["default"]["ENGINE"] == "django.db.backends.postgresql":
|
||||
DATABASES["default"]["ENGINE"] = "django_prometheus.db.backends.postgresql"
|
||||
|
||||
from config.services.unfold import *
|
||||
from config.services.location import *
|
||||
from config.services.notifications import *
|
||||
from config.services.zarinpal import *
|
||||
40
config/settings/development.py
Normal file
40
config/settings/development.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from .base import *
|
||||
|
||||
DEBUG = True
|
||||
|
||||
# Additional development settings
|
||||
INTERNAL_IPS = [
|
||||
"127.0.0.1",
|
||||
]
|
||||
|
||||
# Local frontend/backend wiring
|
||||
ALLOWED_HOSTS = [
|
||||
"127.0.0.1",
|
||||
"localhost",
|
||||
]
|
||||
CORS_ALLOWED_ORIGINS = [
|
||||
"http://localhost:8080",
|
||||
"http://127.0.0.1:8080",
|
||||
]
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
"http://localhost:8080",
|
||||
"http://127.0.0.1:8080",
|
||||
]
|
||||
CSRF_COOKIE_SECURE = False
|
||||
SESSION_COOKIE_SECURE = False
|
||||
SECURE_SSL_REDIRECT = False
|
||||
SECURE_HSTS_SECONDS = 0
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = False
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = False
|
||||
SECURE_BROWSER_XSS_FILTER = False
|
||||
X_FRAME_OPTIONS = "SAMEORIGIN"
|
||||
|
||||
# Email backend for development
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
|
||||
# Disable caching in development
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
|
||||
}
|
||||
}
|
||||
21
config/settings/production.py
Normal file
21
config/settings/production.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from .base import *
|
||||
|
||||
DEBUG = False
|
||||
|
||||
# Security settings for production
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||
SECURE_HSTS_SECONDS = 31536000
|
||||
SECURE_REDIRECT_EXEMPT = []
|
||||
SECURE_SSL_REDIRECT = True
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
X_FRAME_OPTIONS = 'DENY'
|
||||
|
||||
# 🔹 Exempt /metrics from the redirect so Prometheus can scrape over HTTP
|
||||
SECURE_REDIRECT_EXEMPT = [r"^metrics$"]
|
||||
|
||||
# Logging for production
|
||||
# LOGGING['handlers']['file']['filename'] = '/var/log/django/django.log'
|
||||
46
config/settings/test.py
Normal file
46
config/settings/test.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from .base import *
|
||||
|
||||
# Lightweight defaults keep local/CI test runs isolated from production infra.
|
||||
|
||||
TEST_DB_ENGINE = config("TEST_DB_ENGINE", default="django.db.backends.sqlite3")
|
||||
TEST_DB_NAME = config("TEST_DB_NAME", default=str(BASE_DIR / "db.test.sqlite3"))
|
||||
TEST_DB_USER = config("TEST_DB_USER", default="")
|
||||
TEST_DB_PASSWORD = config("TEST_DB_PASSWORD", default="")
|
||||
TEST_DB_HOST = config("TEST_DB_HOST", default="")
|
||||
TEST_DB_PORT = config("TEST_DB_PORT", default="")
|
||||
|
||||
DATABASES["default"] = {
|
||||
"ENGINE": TEST_DB_ENGINE,
|
||||
"NAME": TEST_DB_NAME,
|
||||
"USER": TEST_DB_USER,
|
||||
"PASSWORD": TEST_DB_PASSWORD,
|
||||
"HOST": TEST_DB_HOST,
|
||||
"PORT": TEST_DB_PORT,
|
||||
}
|
||||
|
||||
PASSWORD_HASHERS = [
|
||||
"django.contrib.auth.hashers.MD5PasswordHasher",
|
||||
]
|
||||
|
||||
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
||||
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||
}
|
||||
}
|
||||
|
||||
CELERY_TASK_ALWAYS_EAGER = True
|
||||
CELERY_TASK_EAGER_PROPAGATES = True
|
||||
|
||||
# Tests should not enforce HTTPS-only cookies to simplify client simulations.
|
||||
CSRF_COOKIE_SECURE = False
|
||||
SESSION_COOKIE_SECURE = False
|
||||
|
||||
# Silence verbose INFO logs (e.g., Celery task output) during tests.
|
||||
LOGGING["handlers"]["console"]["level"] = "ERROR" # type: ignore[index]
|
||||
LOGGING["root"]["level"] = "ERROR" # type: ignore[index]
|
||||
if "django" in LOGGING["loggers"]:
|
||||
LOGGING["loggers"]["django"]["level"] = "ERROR" # type: ignore[index]
|
||||
if "apps" in LOGGING["loggers"]:
|
||||
LOGGING["loggers"]["apps"]["level"] = "ERROR" # type: ignore[index]
|
||||
24
config/urls.py
Normal file
24
config/urls.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from ninja import NinjaAPI
|
||||
from config.api import router as api_router
|
||||
|
||||
api = NinjaAPI(
|
||||
title="CS Association API",
|
||||
version="1.0.0",
|
||||
description="API for University Computer Science Association",
|
||||
)
|
||||
|
||||
api.add_router("", api_router)
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/', api.urls),
|
||||
path("", include("django_prometheus.urls")),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
7
config/wsgi.py
Normal file
7
config/wsgi.py
Normal file
@@ -0,0 +1,7 @@
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.development')
|
||||
|
||||
application = get_wsgi_application()
|
||||
Reference in New Issue
Block a user