🐛 fix(config): configure REST Framework settings and add JWT, Swagger, and Redoc views to urls
This commit is contained in:
6 files changed
+208
-10
No files matched your search
+21
-9
@@ -2,6 +2,7 @@ import os
|
||||
from decouple import config
|
||||
from pathlib import Path
|
||||
from .jazzmin import JAZZMIN_SETTINGS
|
||||
from .rest_framework import REST_FRAMEWORK, SIMPLE_JWT, SPECTACULAR_SETTINGS
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
@@ -22,8 +23,10 @@ INSTALLED_APPS = [
|
||||
"django.contrib.staticfiles",
|
||||
# Third-party
|
||||
"rest_framework",
|
||||
'django_filters',
|
||||
'corsheaders',
|
||||
"rest_framework_simplejwt",
|
||||
"django_filters",
|
||||
"corsheaders",
|
||||
"drf_spectacular",
|
||||
# Local
|
||||
"apps.accounts",
|
||||
"apps.events",
|
||||
@@ -40,7 +43,6 @@ INSTALLED_APPS = [
|
||||
|
||||
AUTH_USER_MODEL = "accounts.User"
|
||||
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
@@ -52,7 +54,6 @@ MIDDLEWARE = [
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
|
||||
ROOT_URLCONF = "config.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
@@ -97,22 +98,21 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
# Internationalization
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
TIME_ZONE = 'Asia/Kathmandu'
|
||||
USE_I18N = True
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
STATIC_URL = "/static/"
|
||||
STATICFILES_DIRS = [BASE_DIR / "static"]
|
||||
MEDIA_URL = "/media/"
|
||||
MEDIA_ROOT = BASE_DIR / "media"
|
||||
|
||||
REST_FRAMEWORK = {"DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema"}
|
||||
# REST Framework configuration is imported from rest_framework.py
|
||||
|
||||
# Default primary key field type
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
# CORS Configuration
|
||||
@@ -144,3 +144,15 @@ CORS_ALLOW_HEADERS = [
|
||||
"x-csrftoken",
|
||||
"x-requested-with",
|
||||
]
|
||||
|
||||
# Security Settings
|
||||
if not DEBUG:
|
||||
SECURE_SSL_REDIRECT = True
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
X_FRAME_OPTIONS = 'DENY'
|
||||
SECURE_HSTS_SECONDS = 31536000 # 1 year
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||
SECURE_HSTS_PRELOAD = True
|
||||
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
REST Framework configuration settings
|
||||
"""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
# Use Django's standard `django.contrib.auth` permissions,
|
||||
# or allow read-only access for unauthenticated users.
|
||||
'DEFAULT_PERMISSION_CLASSES': [
|
||||
'rest_framework.permissions.IsAuthenticated',
|
||||
],
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
'rest_framework.authentication.BasicAuthentication',
|
||||
],
|
||||
# Pagination settings
|
||||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
||||
'PAGE_SIZE': 10,
|
||||
# Filtering
|
||||
'DEFAULT_FILTER_BACKENDS': [
|
||||
'django_filters.rest_framework.DjangoFilterBackend',
|
||||
'rest_framework.filters.SearchFilter',
|
||||
'rest_framework.filters.OrderingFilter',
|
||||
],
|
||||
# Throttling
|
||||
'DEFAULT_THROTTLE_CLASSES': [
|
||||
'rest_framework.throttling.AnonRateThrottle',
|
||||
'rest_framework.throttling.UserRateThrottle'
|
||||
],
|
||||
'DEFAULT_THROTTLE_RATES': {
|
||||
'anon': '100/day',
|
||||
'user': '1000/day'
|
||||
},
|
||||
# Versioning
|
||||
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
|
||||
'DEFAULT_VERSION': 'v1',
|
||||
'ALLOWED_VERSIONS': ['v1'],
|
||||
'VERSION_PARAM': 'version',
|
||||
# Schema
|
||||
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
||||
# Renderer/Parser settings
|
||||
'DEFAULT_RENDERER_CLASSES': [
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
'rest_framework.renderers.BrowsableAPIRenderer',
|
||||
],
|
||||
'DEFAULT_PARSER_CLASSES': [
|
||||
'rest_framework.parsers.JSONParser',
|
||||
'rest_framework.parsers.FormParser',
|
||||
'rest_framework.parsers.MultiPartParser'
|
||||
],
|
||||
# Exception handling
|
||||
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
|
||||
# Content negotiation
|
||||
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
|
||||
# Metadata
|
||||
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
|
||||
# Caching
|
||||
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 15, # 15 minutes
|
||||
# Test settings
|
||||
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
|
||||
'TEST_REQUEST_RENDERER_CLASSES': [
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
],
|
||||
}
|
||||
|
||||
# JWT Settings
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
|
||||
'ROTATE_REFRESH_TOKENS': False,
|
||||
'BLACKLIST_AFTER_ROTATION': True,
|
||||
'UPDATE_LAST_LOGIN': False,
|
||||
|
||||
'ALGORITHM': 'HS256',
|
||||
'SIGNING_KEY': None, # Will use Django's SECRET_KEY
|
||||
'VERIFYING_KEY': None,
|
||||
'AUDIENCE': None,
|
||||
'ISSUER': None,
|
||||
|
||||
'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',
|
||||
|
||||
'JTI_CLAIM': 'jti',
|
||||
|
||||
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
|
||||
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
|
||||
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
|
||||
}
|
||||
|
||||
# Spectacular API settings
|
||||
SPECTACULAR_SETTINGS = {
|
||||
'TITLE': 'AlumniVerse API',
|
||||
'DESCRIPTION': 'API documentation for AlumniVerse platform',
|
||||
'VERSION': '1.0.0',
|
||||
'SERVE_INCLUDE_SCHEMA': False,
|
||||
'COMPONENT_SPLIT_REQUEST': True,
|
||||
'SWAGGER_UI_SETTINGS': {
|
||||
'deepLinking': True,
|
||||
'persistAuthorization': True,
|
||||
'displayOperationId': True,
|
||||
},
|
||||
'SWAGGER_UI_DIST': 'SIDECAR',
|
||||
'SWAGGER_UI_FAVICON_HREF': 'SIDECAR',
|
||||
'REDOC_DIST': 'SIDECAR',
|
||||
'PREPROCESSING_HOOKS': [],
|
||||
'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAuthenticated'],
|
||||
'SERVE_AUTHENTICATION': ['rest_framework.authentication.SessionAuthentication'],
|
||||
'SWAGGER_UI_SETTINGS': {
|
||||
'deepLinking': True,
|
||||
'persistAuthorization': True,
|
||||
'displayOperationId': True,
|
||||
},
|
||||
}
|
||||
+40
-1
@@ -15,8 +15,47 @@ Including another URLconf
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.urls import path, include
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from rest_framework_simplejwt.views import (
|
||||
TokenObtainPairView,
|
||||
TokenRefreshView,
|
||||
TokenVerifyView,
|
||||
)
|
||||
from drf_spectacular.views import (
|
||||
SpectacularAPIView,
|
||||
SpectacularRedocView,
|
||||
SpectacularSwaggerView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
|
||||
# API Authentication
|
||||
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
|
||||
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||
path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
|
||||
|
||||
# API Documentation
|
||||
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
|
||||
path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
|
||||
path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
|
||||
|
||||
# API Apps
|
||||
path('api/accounts/', include('apps.accounts.urls')),
|
||||
path('api/events/', include('apps.events.urls')),
|
||||
path('api/news/', include('apps.news.urls')),
|
||||
path('api/jobs/', include('apps.jobs.urls')),
|
||||
path('api/forum/', include('apps.forum.urls')),
|
||||
path('api/mentorship/', include('apps.mentorship.urls')),
|
||||
path('api/achievements/', include('apps.achievements.urls')),
|
||||
path('api/groups/', include('apps.groups.urls')),
|
||||
path('api/messaging/', include('apps.messaging.urls')),
|
||||
path('api/notifications/', include('apps.notifications.urls')),
|
||||
path('api/dashboard/', include('apps.dashboard.urls')),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
@@ -10,6 +10,7 @@ dependencies = [
|
||||
"django-filter>=25.1",
|
||||
"django-jazzmin>=3.0.1",
|
||||
"djangorestframework>=3.16.0",
|
||||
"djangorestframework-simplejwt>=5.5.0",
|
||||
"drf-spectacular>=0.28.0",
|
||||
"pillow>=11.2.1",
|
||||
"python-decouple>=3.8",
|
||||
|
||||
@@ -5,6 +5,7 @@ django-cors-headers==4.7.0
|
||||
django-filter==25.1
|
||||
django-jazzmin==3.0.1
|
||||
djangorestframework==3.16.0
|
||||
djangorestframework-simplejwt==5.3.1
|
||||
drf-spectacular==0.28.0
|
||||
inflection==0.5.1
|
||||
jsonschema==4.24.0
|
||||
|
||||
@@ -12,6 +12,7 @@ dependencies = [
|
||||
{ name = "django-filter" },
|
||||
{ name = "django-jazzmin" },
|
||||
{ name = "djangorestframework" },
|
||||
{ name = "djangorestframework-simplejwt" },
|
||||
{ name = "drf-spectacular" },
|
||||
{ name = "pillow" },
|
||||
{ name = "python-decouple" },
|
||||
@@ -24,6 +25,7 @@ requires-dist = [
|
||||
{ name = "django-filter", specifier = ">=25.1" },
|
||||
{ name = "django-jazzmin", specifier = ">=3.0.1" },
|
||||
{ name = "djangorestframework", specifier = ">=3.16.0" },
|
||||
{ name = "djangorestframework-simplejwt", specifier = ">=5.5.0" },
|
||||
{ name = "drf-spectacular", specifier = ">=0.28.0" },
|
||||
{ name = "pillow", specifier = ">=11.2.1" },
|
||||
{ name = "python-decouple", specifier = ">=3.8" },
|
||||
@@ -110,6 +112,20 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/3e/2448e93f4f87fc9a9f35e73e3c05669e0edd0c2526834686e949bb1fd303/djangorestframework-3.16.0-py3-none-any.whl", hash = "sha256:bea7e9f6b96a8584c5224bfb2e4348dfb3f8b5e34edbecb98da258e892089361", size = 1067305, upload-time = "2025-03-28T14:18:39.489Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "djangorestframework-simplejwt"
|
||||
version = "5.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "djangorestframework" },
|
||||
{ name = "pyjwt" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/db/1e/0d4439d0fa1d93599fbcfc56efdc02cbf012e3a4b4ef90c835e0a51017d4/djangorestframework_simplejwt-5.5.0.tar.gz", hash = "sha256:474a1b737067e6462b3609627a392d13a4da8a08b1f0574104ac6d7b1406f90e", size = 97946, upload-time = "2025-02-26T19:36:01.717Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/42/b4/d1c1750aa7c8cc07e4974275f96b9b9b3a38e95ff734e14b4e97790c8974/djangorestframework_simplejwt-5.5.0-py3-none-any.whl", hash = "sha256:4ef6b38af20cdde4a4a51d1fd8e063cbbabb7b45f149cc885d38d905c5a62edb", size = 103480, upload-time = "2025-02-26T19:36:29.04Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "drf-spectacular"
|
||||
version = "0.28.0"
|
||||
@@ -204,6 +220,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/67/32/32dc030cfa91ca0fc52baebbba2e009bb001122a1daa8b6a79ad830b38d3/pillow-11.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681", size = 2417234, upload-time = "2025-04-12T17:49:08.399Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyjwt"
|
||||
version = "2.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fb/68/ce067f09fca4abeca8771fe667d89cc347d1e99da3e093112ac329c6020e/pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c", size = 78825, upload-time = "2024-08-01T15:01:08.445Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344, upload-time = "2024-08-01T15:01:06.481Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-decouple"
|
||||
version = "3.8"
|
||||
|
||||
Reference in new issue
Block a user