mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 06:11:08 -05:00
Add secret management guide, client-side performance monitoring, and search accessibility enhancements
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols. - Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage. - Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
This commit is contained in:
@@ -33,6 +33,11 @@ from django.views.decorators.http import require_POST
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
from apps.core.logging import log_exception, log_business_event
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Constants
|
||||
PARK_DETAIL_URL = "parks:park_detail"
|
||||
@@ -285,6 +290,12 @@ class ParkListView(HTMXFilterableMixin, ListView):
|
||||
self.filterset = self.filter_class(self.request.GET, queryset=queryset)
|
||||
return self.filterset.qs
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={"operation": "get_filtered_queryset", "filters": filter_params},
|
||||
request=self.request,
|
||||
)
|
||||
messages.error(self.request, f"Error loading parks: {str(e)}")
|
||||
queryset = self.model.objects.none()
|
||||
self.filterset = self.filter_class(self.request.GET, queryset=queryset)
|
||||
@@ -330,6 +341,15 @@ class ParkListView(HTMXFilterableMixin, ListView):
|
||||
return context
|
||||
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={
|
||||
"operation": "get_context_data",
|
||||
"search_query": self.request.GET.get("search", ""),
|
||||
},
|
||||
request=self.request,
|
||||
)
|
||||
messages.error(self.request, f"Error applying filters: {str(e)}")
|
||||
# Ensure filterset exists in error case
|
||||
if not hasattr(self, "filterset"):
|
||||
@@ -478,6 +498,16 @@ def search_parks(request: HttpRequest) -> HttpResponse:
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={
|
||||
"operation": "search_parks",
|
||||
"search_query": request.GET.get("search", ""),
|
||||
"view_mode": request.GET.get("view_mode", "grid"),
|
||||
},
|
||||
request=request,
|
||||
)
|
||||
response = render(
|
||||
request,
|
||||
PARK_LIST_ITEM_TEMPLATE,
|
||||
@@ -505,7 +535,13 @@ def htmx_saved_trips(request: HttpRequest) -> HttpResponse:
|
||||
|
||||
qs = Trip.objects.filter(owner=request.user).order_by("-created_at")
|
||||
trips = list(qs[:10])
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={"operation": "htmx_saved_trips"},
|
||||
request=request,
|
||||
)
|
||||
trips = []
|
||||
return render(request, SAVED_TRIPS_TEMPLATE, {"trips": trips})
|
||||
|
||||
@@ -514,7 +550,13 @@ def _get_session_trip(request: HttpRequest) -> list:
|
||||
raw = request.session.get("trip_parks", [])
|
||||
try:
|
||||
return [int(x) for x in raw]
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={"operation": "get_session_trip", "raw": raw},
|
||||
request=request,
|
||||
)
|
||||
return []
|
||||
|
||||
|
||||
@@ -527,11 +569,21 @@ def _save_session_trip(request: HttpRequest, trip_list: list) -> None:
|
||||
def htmx_add_park_to_trip(request: HttpRequest) -> HttpResponse:
|
||||
"""Add a park id to `request.session['trip_parks']` and return the full trip list partial."""
|
||||
park_id = request.POST.get("park_id")
|
||||
payload = None
|
||||
if not park_id:
|
||||
try:
|
||||
payload = json.loads(request.body.decode("utf-8"))
|
||||
park_id = payload.get("park_id")
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={
|
||||
"operation": "htmx_add_park_to_trip",
|
||||
"payload": request.body.decode("utf-8", errors="replace")[:500],
|
||||
},
|
||||
request=request,
|
||||
)
|
||||
park_id = None
|
||||
|
||||
if not park_id:
|
||||
@@ -539,7 +591,16 @@ def htmx_add_park_to_trip(request: HttpRequest) -> HttpResponse:
|
||||
|
||||
try:
|
||||
pid = int(park_id)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={
|
||||
"operation": "htmx_add_park_to_trip",
|
||||
"park_id": park_id,
|
||||
},
|
||||
request=request,
|
||||
)
|
||||
return HttpResponse("", status=400)
|
||||
|
||||
trip = _get_session_trip(request)
|
||||
@@ -565,11 +626,21 @@ def htmx_add_park_to_trip(request: HttpRequest) -> HttpResponse:
|
||||
def htmx_remove_park_from_trip(request: HttpRequest) -> HttpResponse:
|
||||
"""Remove a park id from `request.session['trip_parks']` and return the updated trip list partial."""
|
||||
park_id = request.POST.get("park_id")
|
||||
payload = None
|
||||
if not park_id:
|
||||
try:
|
||||
payload = json.loads(request.body.decode("utf-8"))
|
||||
park_id = payload.get("park_id")
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={
|
||||
"operation": "htmx_remove_park_from_trip",
|
||||
"payload": request.body.decode("utf-8", errors="replace")[:500],
|
||||
},
|
||||
request=request,
|
||||
)
|
||||
park_id = None
|
||||
|
||||
if not park_id:
|
||||
@@ -577,7 +648,16 @@ def htmx_remove_park_from_trip(request: HttpRequest) -> HttpResponse:
|
||||
|
||||
try:
|
||||
pid = int(park_id)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={
|
||||
"operation": "htmx_remove_park_from_trip",
|
||||
"park_id": park_id,
|
||||
},
|
||||
request=request,
|
||||
)
|
||||
return HttpResponse("", status=400)
|
||||
|
||||
trip = _get_session_trip(request)
|
||||
@@ -605,7 +685,16 @@ def htmx_reorder_parks(request: HttpRequest) -> HttpResponse:
|
||||
try:
|
||||
payload = json.loads(request.body.decode("utf-8"))
|
||||
order = payload.get("order", [])
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={
|
||||
"operation": "htmx_reorder_parks",
|
||||
"payload": request.body.decode("utf-8", errors="replace")[:500],
|
||||
},
|
||||
request=request,
|
||||
)
|
||||
order = request.POST.getlist("order[]")
|
||||
|
||||
# Normalize to ints
|
||||
@@ -613,7 +702,16 @@ def htmx_reorder_parks(request: HttpRequest) -> HttpResponse:
|
||||
for item in order:
|
||||
try:
|
||||
clean_order.append(int(item))
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={
|
||||
"operation": "htmx_reorder_parks",
|
||||
"order_item": item,
|
||||
},
|
||||
request=request,
|
||||
)
|
||||
continue
|
||||
|
||||
_save_session_trip(request, clean_order)
|
||||
@@ -676,7 +774,27 @@ def htmx_optimize_route(request: HttpRequest) -> HttpResponse:
|
||||
total_miles += haversine_miles(
|
||||
a["latitude"], a["longitude"], b["latitude"], b["longitude"]
|
||||
)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
log_exception(
|
||||
logger,
|
||||
e,
|
||||
context={
|
||||
"operation": "htmx_optimize_route",
|
||||
"waypoint_index_a": i,
|
||||
"waypoint_index_b": i + 1,
|
||||
"waypoint_a": {
|
||||
"id": a.get("id"),
|
||||
"latitude": a.get("latitude"),
|
||||
"longitude": a.get("longitude"),
|
||||
},
|
||||
"waypoint_b": {
|
||||
"id": b.get("id"),
|
||||
"latitude": b.get("latitude"),
|
||||
"longitude": b.get("longitude"),
|
||||
},
|
||||
},
|
||||
request=request,
|
||||
)
|
||||
continue
|
||||
|
||||
# Estimate drive time assuming average speed of 60 mph
|
||||
@@ -812,6 +930,18 @@ class ParkCreateView(LoginRequiredMixin, CreateView):
|
||||
|
||||
if service_result["status"] == "auto_approved":
|
||||
self.object = service_result["park"]
|
||||
log_business_event(
|
||||
logger,
|
||||
event_type="park_created",
|
||||
message=f"Park created: {self.object.name} (auto-approved)",
|
||||
context={
|
||||
"park_id": self.object.id,
|
||||
"park_name": self.object.name,
|
||||
"status": "auto_approved",
|
||||
"photo_count": service_result["uploaded_count"],
|
||||
},
|
||||
request=self.request,
|
||||
)
|
||||
messages.success(
|
||||
self.request,
|
||||
f"Successfully created {self.object.name}. "
|
||||
@@ -820,6 +950,16 @@ class ParkCreateView(LoginRequiredMixin, CreateView):
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
elif service_result["status"] == "queued":
|
||||
log_business_event(
|
||||
logger,
|
||||
event_type="park_created",
|
||||
message="Park submission queued for moderation",
|
||||
context={
|
||||
"status": "queued",
|
||||
"park_name": form.cleaned_data.get("name"),
|
||||
},
|
||||
request=self.request,
|
||||
)
|
||||
messages.success(
|
||||
self.request,
|
||||
"Your park submission has been sent for review. "
|
||||
@@ -916,6 +1056,18 @@ class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
||||
|
||||
if service_result["status"] == "auto_approved":
|
||||
self.object = service_result["park"]
|
||||
log_business_event(
|
||||
logger,
|
||||
event_type="park_updated",
|
||||
message=f"Park updated: {self.object.name} (auto-approved)",
|
||||
context={
|
||||
"park_id": self.object.id,
|
||||
"park_name": self.object.name,
|
||||
"status": "auto_approved",
|
||||
"photo_count": service_result["uploaded_count"],
|
||||
},
|
||||
request=self.request,
|
||||
)
|
||||
messages.success(
|
||||
self.request,
|
||||
f"Successfully updated {self.object.name}. "
|
||||
@@ -924,6 +1076,17 @@ class ParkUpdateView(LoginRequiredMixin, UpdateView):
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
elif service_result["status"] == "queued":
|
||||
log_business_event(
|
||||
logger,
|
||||
event_type="park_updated",
|
||||
message=f"Park update queued for moderation: {self.object.name}",
|
||||
context={
|
||||
"park_id": self.object.id,
|
||||
"park_name": self.object.name,
|
||||
"status": "queued",
|
||||
},
|
||||
request=self.request,
|
||||
)
|
||||
messages.success(
|
||||
self.request,
|
||||
f"Your changes to {self.object.name} have been sent for review. "
|
||||
|
||||
Reference in New Issue
Block a user