zeroed-some/localtoast / 02e5e89

Browse files

add cors options handling

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
02e5e89e4526c95e60ff115ad0ea2d3ebf42acbf
Parents
487fdab
Tree
31234b9

3 changed files

StatusFile+-
M backend/api/urls.py 3 0
M backend/api/views.py 72 1
M backend/localtoast/settings.py 31 5
backend/api/urls.pymodified
@@ -5,6 +5,9 @@ urlpatterns = [
5
     # Health check
5
     # Health check
6
     path('health/', views.health_check, name='health_check'),
6
     path('health/', views.health_check, name='health_check'),
7
     
7
     
8
+    # CORS test endpoint
9
+    path('cors-test/', views.cors_test, name='cors_test'),
10
+    
8
     # Restaurant endpoints
11
     # Restaurant endpoints
9
     path('restaurants/', views.RestaurantListCreateView.as_view(), name='restaurant_list_create'),
12
     path('restaurants/', views.RestaurantListCreateView.as_view(), name='restaurant_list_create'),
10
     path('restaurants/nearby/', views.NearbyRestaurantsView.as_view(), name='restaurants_nearby'),
13
     path('restaurants/nearby/', views.NearbyRestaurantsView.as_view(), name='restaurants_nearby'),
backend/api/views.pymodified
@@ -1,4 +1,4 @@
1
-from django.http import JsonResponse
1
+from django.http import JsonResponse, HttpResponse
2
 from django.utils import timezone
2
 from django.utils import timezone
3
 from django.conf import settings
3
 from django.conf import settings
4
 from django.db.models import Q
4
 from django.db.models import Q
@@ -39,6 +39,15 @@ def calculate_distance(lat1, lon1, lat2, lon2):
39
 class NearbyRestaurantsView(APIView):
39
 class NearbyRestaurantsView(APIView):
40
     """Get restaurants near a location"""
40
     """Get restaurants near a location"""
41
     
41
     
42
+    def options(self, request, *args, **kwargs):
43
+        """Handle preflight requests"""
44
+        response = HttpResponse()
45
+        response['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
46
+        response['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
47
+        response['Access-Control-Allow-Headers'] = 'Content-Type, Accept, X-Requested-With'
48
+        response['Access-Control-Max-Age'] = '3600'
49
+        return response
50
+    
42
     def get(self, request):
51
     def get(self, request):
43
         lat = request.query_params.get('lat')
52
         lat = request.query_params.get('lat')
44
         lng = request.query_params.get('lng')
53
         lng = request.query_params.get('lng')
@@ -93,6 +102,15 @@ class RestaurantListCreateView(generics.ListCreateAPIView):
93
     queryset = Restaurant.objects.all()
102
     queryset = Restaurant.objects.all()
94
     serializer_class = RestaurantSerializer
103
     serializer_class = RestaurantSerializer
95
     
104
     
105
+    def options(self, request, *args, **kwargs):
106
+        """Handle preflight requests"""
107
+        response = HttpResponse()
108
+        response['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
109
+        response['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
110
+        response['Access-Control-Allow-Headers'] = 'Content-Type, Accept, X-Requested-With'
111
+        response['Access-Control-Max-Age'] = '3600'
112
+        return response
113
+    
96
     def create(self, request, *args, **kwargs):
114
     def create(self, request, *args, **kwargs):
97
         # Check if restaurant already exists
115
         # Check if restaurant already exists
98
         place_id = request.data.get('place_id')
116
         place_id = request.data.get('place_id')
@@ -109,11 +127,29 @@ class RestaurantDetailView(generics.RetrieveAPIView):
109
     """Get detailed info about a restaurant"""
127
     """Get detailed info about a restaurant"""
110
     queryset = Restaurant.objects.all()
128
     queryset = Restaurant.objects.all()
111
     serializer_class = RestaurantDetailSerializer
129
     serializer_class = RestaurantDetailSerializer
130
+    
131
+    def options(self, request, *args, **kwargs):
132
+        """Handle preflight requests"""
133
+        response = HttpResponse()
134
+        response['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
135
+        response['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
136
+        response['Access-Control-Allow-Headers'] = 'Content-Type, Accept, X-Requested-With'
137
+        response['Access-Control-Max-Age'] = '3600'
138
+        return response
112
 
139
 
113
 
140
 
114
 class RestaurantRatingView(APIView):
141
 class RestaurantRatingView(APIView):
115
     """Add a rating to a restaurant or get all ratings"""
142
     """Add a rating to a restaurant or get all ratings"""
116
     
143
     
144
+    def options(self, request, *args, **kwargs):
145
+        """Handle preflight requests"""
146
+        response = HttpResponse()
147
+        response['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
148
+        response['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
149
+        response['Access-Control-Allow-Headers'] = 'Content-Type, Accept, X-Requested-With'
150
+        response['Access-Control-Max-Age'] = '3600'
151
+        return response
152
+    
117
     def get(self, request, pk):
153
     def get(self, request, pk):
118
         try:
154
         try:
119
             restaurant = Restaurant.objects.get(pk=pk)
155
             restaurant = Restaurant.objects.get(pk=pk)
@@ -152,6 +188,15 @@ class RestaurantRatingView(APIView):
152
 class RestaurantToastStatusView(APIView):
188
 class RestaurantToastStatusView(APIView):
153
     """Update restaurant's toast status"""
189
     """Update restaurant's toast status"""
154
     
190
     
191
+    def options(self, request, *args, **kwargs):
192
+        """Handle preflight requests"""
193
+        response = HttpResponse()
194
+        response['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
195
+        response['Access-Control-Allow-Methods'] = 'PATCH, OPTIONS'
196
+        response['Access-Control-Allow-Headers'] = 'Content-Type, Accept, X-Requested-With'
197
+        response['Access-Control-Max-Age'] = '3600'
198
+        return response
199
+    
155
     def patch(self, request, pk):
200
     def patch(self, request, pk):
156
         try:
201
         try:
157
             restaurant = Restaurant.objects.get(pk=pk)
202
             restaurant = Restaurant.objects.get(pk=pk)
@@ -181,6 +226,15 @@ class RestaurantToastStatusView(APIView):
181
 class SearchPlacesView(APIView):
226
 class SearchPlacesView(APIView):
182
     """Search for places that might serve toast"""
227
     """Search for places that might serve toast"""
183
     
228
     
229
+    def options(self, request, *args, **kwargs):
230
+        """Handle preflight requests"""
231
+        response = HttpResponse()
232
+        response['Access-Control-Allow-Origin'] = request.headers.get('Origin', '*')
233
+        response['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
234
+        response['Access-Control-Allow-Headers'] = 'Content-Type, Accept, X-Requested-With'
235
+        response['Access-Control-Max-Age'] = '3600'
236
+        return response
237
+    
184
     def get(self, request):
238
     def get(self, request):
185
         lat = request.query_params.get('lat')
239
         lat = request.query_params.get('lat')
186
         lng = request.query_params.get('lng')
240
         lng = request.query_params.get('lng')
@@ -353,6 +407,23 @@ class SearchPlacesView(APIView):
353
         ]
407
         ]
354
 
408
 
355
 
409
 
410
+@api_view(['GET', 'OPTIONS'])
411
+def cors_test(request):
412
+    """Test endpoint for CORS"""
413
+    if request.method == 'OPTIONS':
414
+        response = HttpResponse()
415
+        response['Access-Control-Allow-Origin'] = '*'
416
+        response['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
417
+        response['Access-Control-Allow-Headers'] = '*'
418
+        return response
419
+    
420
+    return Response({
421
+        'status': 'CORS test successful',
422
+        'origin': request.headers.get('Origin', 'No origin header'),
423
+        'method': request.method
424
+    })
425
+
426
+
356
 @api_view(['POST'])
427
 @api_view(['POST'])
357
 def seed_data(request):
428
 def seed_data(request):
358
     """Seed the database with sample restaurants"""
429
     """Seed the database with sample restaurants"""
backend/localtoast/settings.pymodified
@@ -31,7 +31,7 @@ INSTALLED_APPS = [
31
 
31
 
32
 MIDDLEWARE = [
32
 MIDDLEWARE = [
33
     'django.middleware.security.SecurityMiddleware',
33
     'django.middleware.security.SecurityMiddleware',
34
-    'corsheaders.middleware.CorsMiddleware',
34
+    'corsheaders.middleware.CorsMiddleware',  # Must be before CommonMiddleware
35
     'django.middleware.common.CommonMiddleware',
35
     'django.middleware.common.CommonMiddleware',
36
     'django.middleware.csrf.CsrfViewMiddleware',
36
     'django.middleware.csrf.CsrfViewMiddleware',
37
     'django.contrib.sessions.middleware.SessionMiddleware',
37
     'django.contrib.sessions.middleware.SessionMiddleware',
@@ -106,7 +106,7 @@ REST_FRAMEWORK = {
106
     ],
106
     ],
107
 }
107
 }
108
 
108
 
109
-# CORS settings
109
+# CORS settings - Updated for better compatibility
110
 CORS_ALLOWED_ORIGINS = [
110
 CORS_ALLOWED_ORIGINS = [
111
     "http://localhost:3000",
111
     "http://localhost:3000",
112
     "http://localhost:3001",
112
     "http://localhost:3001",
@@ -116,9 +116,35 @@ CORS_ALLOWED_ORIGINS = [
116
     "https://localtoast.fyi",
116
     "https://localtoast.fyi",
117
 ]
117
 ]
118
 
118
 
119
-# Allow CORS during development
119
+# Add frontend URL from environment if provided
120
-if DEBUG:
120
+if os.getenv('FRONTEND_URL'):
121
-    CORS_ALLOW_ALL_ORIGINS = True
121
+    CORS_ALLOWED_ORIGINS.append(os.getenv('FRONTEND_URL'))
122
+
123
+# Explicit CORS configuration
124
+CORS_ALLOW_ALL_ORIGINS = False  # Never True in production
125
+CORS_ALLOW_CREDENTIALS = True
126
+CORS_PREFLIGHT_MAX_AGE = 86400
127
+
128
+CORS_ALLOW_METHODS = [
129
+    'DELETE',
130
+    'GET',
131
+    'OPTIONS',
132
+    'PATCH',
133
+    'POST',
134
+    'PUT',
135
+]
136
+
137
+CORS_ALLOW_HEADERS = [
138
+    'accept',
139
+    'accept-encoding',
140
+    'authorization',
141
+    'content-type',
142
+    'dnt',
143
+    'origin',
144
+    'user-agent',
145
+    'x-csrftoken',
146
+    'x-requested-with',
147
+]
122
 
148
 
123
 # Media files
149
 # Media files
124
 MEDIA_URL = '/media/'
150
 MEDIA_URL = '/media/'