""" Main URL Configuration for the Django Backend Starter Template. """ import os import re import mimetypes from django.contrib import admin from django.urls import path, include, re_path from django.conf import settings from django.conf.urls.i18n import i18n_patterns from django.http import StreamingHttpResponse, HttpResponse, Http404 from django.views.static import serve as django_static_serve from django.views.generic import RedirectView from drf_spectacular.views import ( SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView, ) from rest_framework_simplejwt.views import ( TokenObtainPairView, TokenRefreshView, TokenVerifyView, ) from rest_framework import permissions from utils.admin import project_admin_site, HomeView # API v1 Patterns api_v1_patterns = [ # Auth & Identity endpoints path('auth/', include('apps.account.urls_auth')), # Institution Profiles path('profiles/', include('apps.profiles.urls')), # Smart Geo-Atlas & Spatial Map path('map/', include('apps.geo_map.urls')), # International Content Management System (CMS) path('cms/', include('apps.cms.urls')), # Collaborative Projects & Kanban path('projects/', include('apps.projects.urls')), # Real-Time Messaging & Chat path('chat/', include('apps.chat.urls')), # Events & Programs Calendar path('events/', include('apps.events.urls')), # Inter-Center Video Meetings & Scheduling path('meetings/', include('apps.meetings.urls')), path('', include('apps.api.urls')), path('account/', include('apps.account.urls')), path('settings/', include('dynamic_preferences.urls')), ] urlpatterns = [ # Language prefix routing for Admin path("i18n/", include("django.conf.urls.i18n")), # REST API Endpoints path("api/v1/", include(api_v1_patterns)), # OpenAPI 3.0 & Interactive Documentation (matching swagger-api-guide.md) 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"), # Backward compatibility redirect path("swagger/", RedirectView.as_view(url="/api/schema/swagger-ui/", permanent=False)), ] # Admin URLs wrapped in i18n urlpatterns += i18n_patterns( path("admin/", project_admin_site.urls), prefix_default_language=True, ) # Static & Media streaming for development mode if settings.DEBUG: def ranged_static_serve(request, path, document_root=None, **kwargs): fullpath = os.path.join(document_root, path) if not os.path.exists(fullpath) or os.path.isdir(fullpath): raise Http404("File not found") range_header = request.META.get('HTTP_RANGE', '').strip() if not range_header: return django_static_serve(request, path, document_root=document_root, **kwargs) size = os.path.getsize(fullpath) content_type, encoding = mimetypes.guess_type(fullpath) content_type = content_type or 'application/octet-stream' match = re.match(r'bytes=(\d+)-(\d*)', range_header) if not match: return HttpResponse("Invalid Range Header", status=400) first_byte, last_byte = match.groups() first_byte = int(first_byte) if first_byte else 0 last_byte = int(last_byte) if last_byte else size - 1 if first_byte >= size: return HttpResponse("Requested Range Not Satisfiable", status=416) if last_byte >= size: last_byte = size - 1 length = last_byte - first_byte + 1 def file_iterator(file_path, offset, bytes_to_read, chunk_size=8192): with open(file_path, 'rb') as f: f.seek(offset) remaining = bytes_to_read while remaining > 0: to_read = min(chunk_size, remaining) data = f.read(to_read) if not data: break yield data remaining -= len(data) response = StreamingHttpResponse( file_iterator(fullpath, first_byte, length), status=206, content_type=content_type ) response['Content-Range'] = f'bytes {first_byte}-{last_byte}/{size}' response['Content-Length'] = str(length) response['Accept-Ranges'] = 'bytes' return response urlpatterns += [ re_path(r'^%s(?P.*)$' % re.escape(settings.MEDIA_URL.lstrip('/')), ranged_static_serve, {'document_root': settings.MEDIA_ROOT}), ]