fix(firebase): fix save fcm token logic
This commit is contained in:
1 parent
929faab1fa
commit
8f0ce113b4
6 files changed
+275
-170
No files matched your search
@@ -699,11 +699,11 @@ def edit_course_tracking(request):
|
||||
# Update tracking fields
|
||||
start_date = request.POST.get("start_date")
|
||||
if start_date:
|
||||
tracking.start_date = start_date
|
||||
tracking.start_date = datetime.strptime(start_date, "%Y-%m-%d").date()
|
||||
|
||||
expected_end_date = request.POST.get("expected_end_date")
|
||||
if expected_end_date:
|
||||
tracking.expected_end_date = expected_end_date
|
||||
tracking.expected_end_date = datetime.strptime(expected_end_date, "%Y-%m-%d").date()
|
||||
|
||||
current_period = request.POST.get("current_period")
|
||||
if current_period:
|
||||
|
||||
+32
-4
@@ -1,5 +1,7 @@
|
||||
from django.http import HttpResponse
|
||||
from django.http import HttpResponse, HttpResponseServerError
|
||||
from django.template.loader import render_to_string
|
||||
import re
|
||||
from django.middleware.csrf import CsrfViewMiddleware
|
||||
|
||||
|
||||
class HTTP505Middleware:
|
||||
@@ -11,9 +13,13 @@ class HTTP505Middleware:
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
# Process the request and get the response
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
# Simple check for HTTP version
|
||||
http_version = request.META.get("SERVER_PROTOCOL", "")
|
||||
if http_version and http_version.startswith("HTTP/") and http_version > "HTTP/2.0":
|
||||
# This is a simplistic check - in reality, you'd want to handle this more gracefully
|
||||
return HttpResponseServerError("HTTP Version Not Supported", content_type="text/plain")
|
||||
|
||||
return self.get_response(request)
|
||||
|
||||
def process_exception(self, request, exception):
|
||||
# Check if exception is related to HTTP version
|
||||
@@ -27,3 +33,25 @@ class HTTP505Middleware:
|
||||
response_content = render_to_string("505.html", context, request)
|
||||
return HttpResponse(response_content, status=505)
|
||||
return None
|
||||
|
||||
|
||||
class CSRFExemptMiddleware(CsrfViewMiddleware):
|
||||
"""
|
||||
Middleware to exempt specific URLs from CSRF protection.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
self.exempt_urls = [
|
||||
re.compile(r'^/saveFCMToken/$'),
|
||||
]
|
||||
super().__init__(get_response)
|
||||
|
||||
def process_view(self, request, callback, callback_args, callback_kwargs):
|
||||
# Check if the path matches any of the exempt URLs
|
||||
path = request.path_info
|
||||
if any(url.match(path) for url in self.exempt_urls):
|
||||
return None # Skip CSRF validation
|
||||
|
||||
# Otherwise, continue with regular CSRF validation
|
||||
return super().process_view(request, callback, callback_args, callback_kwargs)
|
||||
+102
-127
@@ -126,14 +126,12 @@ class Course(models.Model):
|
||||
verbose_name = "Course"
|
||||
verbose_name_plural = "Courses"
|
||||
indexes = [
|
||||
models.Index(fields=["name"]),
|
||||
models.Index(fields=["code"]),
|
||||
models.Index(fields=["is_active"]),
|
||||
models.Index(fields=["duration_type"]),
|
||||
# For active course lookups
|
||||
models.Index(fields=["name", "is_active"]),
|
||||
# For active course lookups by code
|
||||
models.Index(fields=["code", "is_active"]),
|
||||
models.Index(fields=['name']),
|
||||
models.Index(fields=['code']),
|
||||
models.Index(fields=['is_active']),
|
||||
models.Index(fields=['duration_type']),
|
||||
models.Index(fields=['name', 'is_active']), # For active course lookups
|
||||
models.Index(fields=['code', 'is_active']), # For active course lookups by code
|
||||
]
|
||||
|
||||
def cleanup_orphaned_data(self):
|
||||
@@ -224,13 +222,11 @@ class Subject(models.Model):
|
||||
unique_together = ["name", "course", "period_or_year"]
|
||||
ordering = ["course", "period_or_year", "name"]
|
||||
indexes = [
|
||||
models.Index(fields=["course"]),
|
||||
models.Index(fields=["period_or_year"]),
|
||||
models.Index(fields=["name"]),
|
||||
# For course period subjects
|
||||
models.Index(fields=["course", "period_or_year"]),
|
||||
# For subject lookups
|
||||
models.Index(fields=["name", "course"]),
|
||||
models.Index(fields=['course']),
|
||||
models.Index(fields=['period_or_year']),
|
||||
models.Index(fields=['name']),
|
||||
models.Index(fields=['course', 'period_or_year']), # For course period subjects
|
||||
models.Index(fields=['name', 'course']), # For subject lookups
|
||||
]
|
||||
|
||||
def cleanup_orphaned_data(self):
|
||||
@@ -411,7 +407,7 @@ class Student(AbstractUser):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
# Update related records in the same transaction
|
||||
if hasattr(self, "course_trackings"):
|
||||
if hasattr(self, 'course_trackings'):
|
||||
for tracking in self.course_trackings.all():
|
||||
tracking.update_completion_percentage()
|
||||
except ValidationError as e:
|
||||
@@ -424,7 +420,7 @@ class Student(AbstractUser):
|
||||
if self.phone and not self.phone.isdigit():
|
||||
raise ValidationError("Phone number must contain only digits")
|
||||
|
||||
if self.email and "@" not in self.email:
|
||||
if self.email and '@' not in self.email:
|
||||
raise ValidationError("Invalid email format")
|
||||
|
||||
if self.birth_date and self.birth_date > date.today():
|
||||
@@ -482,11 +478,11 @@ class Student(AbstractUser):
|
||||
verbose_name = "Student"
|
||||
verbose_name_plural = "Students"
|
||||
indexes = [
|
||||
models.Index(fields=["phone"]),
|
||||
models.Index(fields=["status"]),
|
||||
models.Index(fields=["course"]),
|
||||
models.Index(fields=["current_period"]),
|
||||
models.Index(fields=["joining_date"]),
|
||||
models.Index(fields=['phone']),
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['course']),
|
||||
models.Index(fields=['current_period']),
|
||||
models.Index(fields=['joining_date']),
|
||||
]
|
||||
|
||||
|
||||
@@ -549,9 +545,7 @@ class Staff(AbstractUser):
|
||||
help_text="Course for which this staff member is HOD",
|
||||
)
|
||||
courses_taught = models.ManyToManyField(Course, related_name="teachers", blank=True)
|
||||
meetings = models.ManyToManyField(
|
||||
"TeacherParentMeeting", related_name="staff_members", blank=True
|
||||
)
|
||||
meetings = models.ManyToManyField("TeacherParentMeeting", related_name="staff_members", blank=True)
|
||||
|
||||
USERNAME_FIELD = "phone"
|
||||
REQUIRED_FIELDS = ["name"]
|
||||
@@ -612,7 +606,7 @@ class Staff(AbstractUser):
|
||||
if self.phone and not self.phone.isdigit():
|
||||
raise ValidationError("Phone number must contain only digits")
|
||||
|
||||
if self.email and "@" not in self.email:
|
||||
if self.email and '@' not in self.email:
|
||||
raise ValidationError("Invalid email format")
|
||||
|
||||
if self.birth_date and self.birth_date > date.today():
|
||||
@@ -663,16 +657,14 @@ class Staff(AbstractUser):
|
||||
verbose_name = "Staff"
|
||||
verbose_name_plural = "Staffs"
|
||||
indexes = [
|
||||
models.Index(fields=["phone"]),
|
||||
models.Index(fields=["designation"]),
|
||||
models.Index(fields=["is_active"]),
|
||||
models.Index(fields=["course"]),
|
||||
models.Index(fields=['phone']),
|
||||
models.Index(fields=['designation']),
|
||||
models.Index(fields=['is_active']),
|
||||
models.Index(fields=['course']),
|
||||
# Add these composite indexes
|
||||
# For active staff by role
|
||||
models.Index(fields=["designation", "is_active"]),
|
||||
models.Index(fields=["course", "is_active"]), # For course staff
|
||||
# For active staff lookups
|
||||
models.Index(fields=["phone", "is_active"]),
|
||||
models.Index(fields=['designation', 'is_active']), # For active staff by role
|
||||
models.Index(fields=['course', 'is_active']), # For course staff
|
||||
models.Index(fields=['phone', 'is_active']), # For active staff lookups
|
||||
]
|
||||
|
||||
|
||||
@@ -703,15 +695,14 @@ class Routine(models.Model):
|
||||
"period_or_year",
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=["course"]),
|
||||
models.Index(fields=["subject"]),
|
||||
models.Index(fields=["teacher"]),
|
||||
models.Index(fields=["is_active"]),
|
||||
models.Index(fields=["period_or_year"]),
|
||||
# For course routines
|
||||
models.Index(fields=["course", "period_or_year"]),
|
||||
models.Index(fields=["teacher", "is_active"]), # For teacher schedules
|
||||
models.Index(fields=["subject", "is_active"]), # For subject schedules
|
||||
models.Index(fields=['course']),
|
||||
models.Index(fields=['subject']),
|
||||
models.Index(fields=['teacher']),
|
||||
models.Index(fields=['is_active']),
|
||||
models.Index(fields=['period_or_year']),
|
||||
models.Index(fields=['course', 'period_or_year']), # For course routines
|
||||
models.Index(fields=['teacher', 'is_active']), # For teacher schedules
|
||||
models.Index(fields=['subject', 'is_active']), # For subject schedules
|
||||
]
|
||||
|
||||
|
||||
@@ -745,17 +736,14 @@ class Attendance(models.Model):
|
||||
verbose_name = "Attendance"
|
||||
verbose_name_plural = "Attendances"
|
||||
indexes = [
|
||||
models.Index(fields=["date"]),
|
||||
models.Index(fields=["routine"]),
|
||||
models.Index(fields=["teacher"]),
|
||||
models.Index(fields=["class_status"]),
|
||||
models.Index(fields=['date']),
|
||||
models.Index(fields=['routine']),
|
||||
models.Index(fields=['teacher']),
|
||||
models.Index(fields=['class_status']),
|
||||
# Add these composite indexes
|
||||
# For date-based routine lookups
|
||||
models.Index(fields=["date", "routine"]),
|
||||
# For teacher attendance history
|
||||
models.Index(fields=["teacher", "date"]),
|
||||
# For class status filtering
|
||||
models.Index(fields=["routine", "class_status"]),
|
||||
models.Index(fields=['date', 'routine']), # For date-based routine lookups
|
||||
models.Index(fields=['teacher', 'date']), # For teacher attendance history
|
||||
models.Index(fields=['routine', 'class_status']), # For class status filtering
|
||||
]
|
||||
|
||||
|
||||
@@ -781,15 +769,13 @@ class AttendanceRecord(models.Model):
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=["attendance"]),
|
||||
models.Index(fields=["student"]),
|
||||
models.Index(fields=["student_attend"]),
|
||||
models.Index(fields=['attendance']),
|
||||
models.Index(fields=['student']),
|
||||
models.Index(fields=['student_attend']),
|
||||
# Add these composite indexes
|
||||
# For attendance filtering
|
||||
models.Index(fields=["student", "student_attend"]),
|
||||
models.Index(fields=["attendance", "student"]), # For attendance lookups
|
||||
# For attendance status
|
||||
models.Index(fields=["attendance", "student_attend"]),
|
||||
models.Index(fields=['student', 'student_attend']), # For attendance filtering
|
||||
models.Index(fields=['attendance', 'student']), # For attendance lookups
|
||||
models.Index(fields=['attendance', 'student_attend']), # For attendance status
|
||||
]
|
||||
|
||||
|
||||
@@ -854,14 +840,13 @@ class StudentLeave(models.Model):
|
||||
verbose_name_plural = "Student Leaves"
|
||||
ordering = ["-created_at"]
|
||||
indexes = [
|
||||
models.Index(fields=["student"]),
|
||||
models.Index(fields=["status"]),
|
||||
models.Index(fields=["start_date"]),
|
||||
models.Index(fields=["end_date"]),
|
||||
# For student leave status
|
||||
models.Index(fields=["student", "status"]),
|
||||
models.Index(fields=["start_date", "end_date"]), # For date range queries
|
||||
models.Index(fields=["status", "start_date"]), # For pending leaves
|
||||
models.Index(fields=['student']),
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['start_date']),
|
||||
models.Index(fields=['end_date']),
|
||||
models.Index(fields=['student', 'status']), # For student leave status
|
||||
models.Index(fields=['start_date', 'end_date']), # For date range queries
|
||||
models.Index(fields=['status', 'start_date']), # For pending leaves
|
||||
]
|
||||
|
||||
|
||||
@@ -1098,35 +1083,34 @@ class CourseTracking(models.Model):
|
||||
|
||||
# Get completed subjects from SubjectProgress
|
||||
completed_subjects = SubjectProgress.objects.filter(
|
||||
student=self.student, subject__course=self.course, status="Completed"
|
||||
student=self.student,
|
||||
subject__course=self.course,
|
||||
status="Completed"
|
||||
).count()
|
||||
|
||||
# Get attendance percentage for active subjects
|
||||
attended_classes = AttendanceRecord.objects.filter(
|
||||
student=self.student,
|
||||
attendance__routine__subject__course=self.course,
|
||||
student_attend=True,
|
||||
student_attend=True
|
||||
).count()
|
||||
|
||||
total_classes = AttendanceRecord.objects.filter(
|
||||
student=self.student, attendance__routine__subject__course=self.course
|
||||
student=self.student,
|
||||
attendance__routine__subject__course=self.course
|
||||
).count()
|
||||
|
||||
# Calculate weighted completion percentage
|
||||
subject_weight = 0.7 # 70% weight to subject completion
|
||||
attendance_weight = 0.3 # 30% weight to attendance
|
||||
|
||||
subject_percentage = (
|
||||
(completed_subjects / total_subjects) * 100 if total_subjects > 0 else 0
|
||||
)
|
||||
attendance_percentage = (
|
||||
(attended_classes / total_classes) * 100 if total_classes > 0 else 0
|
||||
)
|
||||
subject_percentage = (completed_subjects / total_subjects) * 100 if total_subjects > 0 else 0
|
||||
attendance_percentage = (attended_classes / total_classes) * 100 if total_classes > 0 else 0
|
||||
|
||||
# Calculate final percentage
|
||||
completion_percentage = int(
|
||||
(subject_percentage * subject_weight)
|
||||
+ (attendance_percentage * attendance_weight)
|
||||
(subject_percentage * subject_weight) +
|
||||
(attendance_percentage * attendance_weight)
|
||||
)
|
||||
|
||||
# Ensure percentage is between 0 and 100
|
||||
@@ -1208,12 +1192,12 @@ class CourseTracking(models.Model):
|
||||
try:
|
||||
self.clean()
|
||||
# Update completion percentage without triggering save again
|
||||
if not hasattr(self, "_updating_completion"):
|
||||
if not hasattr(self, '_updating_completion'):
|
||||
self._updating_completion = True
|
||||
try:
|
||||
self.completion_percentage = self.update_completion_percentage()
|
||||
finally:
|
||||
delattr(self, "_updating_completion")
|
||||
delattr(self, '_updating_completion')
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
except Exception as e:
|
||||
@@ -1289,17 +1273,17 @@ class CourseTracking(models.Model):
|
||||
unique_together = ["student", "course"]
|
||||
ordering = ["-created_at"]
|
||||
indexes = [
|
||||
models.Index(fields=["student"]),
|
||||
models.Index(fields=["course"]),
|
||||
models.Index(fields=["progress_status"]),
|
||||
models.Index(fields=["current_period"]),
|
||||
models.Index(fields=["enrollment_date"]),
|
||||
models.Index(fields=["student", "progress_status"]),
|
||||
models.Index(fields=["course", "progress_status"]),
|
||||
models.Index(fields=["student", "course", "progress_status"]),
|
||||
models.Index(fields=["completion_percentage"]),
|
||||
models.Index(fields=["start_date", "expected_end_date"]),
|
||||
models.Index(fields=["current_period", "progress_status"]),
|
||||
models.Index(fields=['student']),
|
||||
models.Index(fields=['course']),
|
||||
models.Index(fields=['progress_status']),
|
||||
models.Index(fields=['current_period']),
|
||||
models.Index(fields=['enrollment_date']),
|
||||
models.Index(fields=['student', 'progress_status']),
|
||||
models.Index(fields=['course', 'progress_status']),
|
||||
models.Index(fields=['student', 'course', 'progress_status']),
|
||||
models.Index(fields=['completion_percentage']),
|
||||
models.Index(fields=['start_date', 'expected_end_date']),
|
||||
models.Index(fields=['current_period', 'progress_status']),
|
||||
]
|
||||
|
||||
|
||||
@@ -1342,13 +1326,12 @@ class ResetToken(models.Model):
|
||||
|
||||
class OTPAttempt(models.Model):
|
||||
"""Model to track OTP attempts for rate limiting"""
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="otp_attempts",
|
||||
related_name='otp_attempts',
|
||||
null=True,
|
||||
blank=True,
|
||||
blank=True
|
||||
)
|
||||
identifier = models.CharField(max_length=255) # phone or email
|
||||
attempts = models.IntegerField(default=0)
|
||||
@@ -1358,9 +1341,9 @@ class OTPAttempt(models.Model):
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=["identifier"]),
|
||||
models.Index(fields=["is_locked"]),
|
||||
models.Index(fields=["lock_until"]),
|
||||
models.Index(fields=['identifier']),
|
||||
models.Index(fields=['is_locked']),
|
||||
models.Index(fields=['lock_until']),
|
||||
]
|
||||
|
||||
def increment_attempts(self):
|
||||
@@ -1482,16 +1465,15 @@ class Parent(AbstractUser):
|
||||
|
||||
# Clean up notification tokens
|
||||
self.fcm_token = None
|
||||
self.save(update_fields=["fcm_token"])
|
||||
self.save(update_fields=['fcm_token'])
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Parent"
|
||||
verbose_name_plural = "Parents"
|
||||
indexes = [
|
||||
models.Index(fields=["phone"]),
|
||||
models.Index(fields=["is_active"]),
|
||||
# For active parent lookups
|
||||
models.Index(fields=["phone", "is_active"]),
|
||||
models.Index(fields=['phone']),
|
||||
models.Index(fields=['is_active']),
|
||||
models.Index(fields=['phone', 'is_active']), # For active parent lookups
|
||||
]
|
||||
|
||||
|
||||
@@ -1525,11 +1507,11 @@ class TeacherParentMeeting(models.Model):
|
||||
ordering = ["-meeting_date", "-meeting_time"]
|
||||
unique_together = ["meeting_date", "meeting_time"]
|
||||
indexes = [
|
||||
models.Index(fields=["meeting_date"]),
|
||||
models.Index(fields=["status"]),
|
||||
models.Index(fields=["is_online"]),
|
||||
models.Index(fields=["meeting_date", "status"]), # For upcoming meetings
|
||||
models.Index(fields=["status", "is_online"]), # For online meetings
|
||||
models.Index(fields=['meeting_date']),
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['is_online']),
|
||||
models.Index(fields=['meeting_date', 'status']), # For upcoming meetings
|
||||
models.Index(fields=['status', 'is_online']), # For online meetings
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
@@ -1586,29 +1568,22 @@ class TeacherParentMeeting(models.Model):
|
||||
|
||||
class SubjectProgress(models.Model):
|
||||
"""Model for tracking student progress in individual subjects"""
|
||||
|
||||
student = models.ForeignKey(
|
||||
Student, on_delete=models.CASCADE, related_name="subject_progress"
|
||||
)
|
||||
subject = models.ForeignKey(
|
||||
Subject, on_delete=models.CASCADE, related_name="student_progress"
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=20, choices=PROGRESS_STATUS_CHOICES, default="Not Started"
|
||||
)
|
||||
student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='subject_progress')
|
||||
subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='student_progress')
|
||||
status = models.CharField(max_length=20, choices=PROGRESS_STATUS_CHOICES, default="Not Started")
|
||||
completion_percentage = models.IntegerField(default=0)
|
||||
last_updated = models.DateTimeField(auto_now=True)
|
||||
notes = models.TextField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ["student", "subject"]
|
||||
ordering = ["-last_updated"]
|
||||
unique_together = ['student', 'subject']
|
||||
ordering = ['-last_updated']
|
||||
indexes = [
|
||||
models.Index(fields=["student"]),
|
||||
models.Index(fields=["subject"]),
|
||||
models.Index(fields=["status"]),
|
||||
models.Index(fields=["student", "subject"]),
|
||||
models.Index(fields=["student", "status"]),
|
||||
models.Index(fields=['student']),
|
||||
models.Index(fields=['subject']),
|
||||
models.Index(fields=['status']),
|
||||
models.Index(fields=['student', 'subject']),
|
||||
models.Index(fields=['student', 'status']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
|
||||
+1
-2
@@ -505,9 +505,8 @@ def delete_notice(request, notice_id):
|
||||
return JsonResponse({"success": False, "error": "Invalid request method"})
|
||||
|
||||
|
||||
@login_required
|
||||
@csrf_exempt
|
||||
@require_POST
|
||||
@csrf_exempt
|
||||
def save_fcm_token(request):
|
||||
"""Save FCM token to database"""
|
||||
try:
|
||||
|
||||
+116
-35
@@ -1,4 +1,82 @@
|
||||
// Get CSRF token from cookie
|
||||
// Get CSRF token from cookie or any other available source
|
||||
function getCSRFToken() {
|
||||
// Try getting from cookie
|
||||
let token = getCookie('csrftoken');
|
||||
|
||||
// If not in cookie, try getting from meta tag (Django often puts it there)
|
||||
if (!token) {
|
||||
const metaTag = document.querySelector('meta[name="csrf-token"]');
|
||||
if (metaTag) {
|
||||
token = metaTag.getAttribute('content');
|
||||
}
|
||||
}
|
||||
|
||||
// Try getting from a hidden input field with name="csrfmiddlewaretoken"
|
||||
if (!token) {
|
||||
const inputField = document.querySelector('input[name="csrfmiddlewaretoken"]');
|
||||
if (inputField) {
|
||||
token = inputField.value;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('CSRF Token found:', token ? 'Yes (length: ' + token.length + ')' : 'No');
|
||||
return token || '';
|
||||
}
|
||||
|
||||
// CSRF-safe fetch utility function
|
||||
async function csrfFetch(url, options = {}) {
|
||||
// Default options
|
||||
const defaultOptions = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
credentials: 'include'
|
||||
};
|
||||
|
||||
// Merge provided options with defaults
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
headers: {
|
||||
...defaultOptions.headers,
|
||||
...options.headers
|
||||
}
|
||||
};
|
||||
|
||||
// Add CSRF token if not explicitly provided and it's a mutating request method
|
||||
const method = mergedOptions.method ? mergedOptions.method.toUpperCase() : 'GET';
|
||||
const needsCSRF = ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method);
|
||||
|
||||
if (needsCSRF && !mergedOptions.headers['X-CSRFToken']) {
|
||||
const csrfToken = getCSRFToken();
|
||||
if (csrfToken) {
|
||||
// Set token in exactly the format Django expects (X-CSRFToken)
|
||||
// Django is case-sensitive and specifically looks for X-CSRFToken
|
||||
mergedOptions.headers['X-CSRFToken'] = csrfToken;
|
||||
} else {
|
||||
console.warn('No CSRF token available for a mutating request to:', url);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`Making ${method} request to ${url} with headers:`, mergedOptions.headers);
|
||||
const response = await fetch(url, mergedOptions);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('Server response:', response.status, errorText);
|
||||
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Fetch error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Original getCookie function
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
@@ -71,43 +149,30 @@ async function getDeviceToken() {
|
||||
|
||||
console.log('Got FCM token:', token);
|
||||
|
||||
// Get CSRF token
|
||||
const csrftoken = getCookie('csrftoken');
|
||||
if (!csrftoken) {
|
||||
throw new Error('CSRF token not found');
|
||||
// Simple approach: send token to server using basic fetch
|
||||
try {
|
||||
const response = await fetch('/app/saveFCMToken/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ token: token })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log('FCM Token saved successfully');
|
||||
} else {
|
||||
console.error('Failed to save FCM token:', response.status);
|
||||
}
|
||||
|
||||
return token;
|
||||
} catch (fetchError) {
|
||||
console.error('Error saving FCM token:', fetchError);
|
||||
return token; // Return token even if saving failed
|
||||
}
|
||||
|
||||
// Send the token to your backend for storage
|
||||
const response = await fetch('/app/saveFCMToken/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrftoken
|
||||
},
|
||||
body: JSON.stringify({ token: token })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to save token to server');
|
||||
}
|
||||
|
||||
console.log('FCM Token saved successfully');
|
||||
return token;
|
||||
} catch (error) {
|
||||
console.error('Error in getDeviceToken:', error);
|
||||
try {
|
||||
// Clean up and retry on failure
|
||||
await messaging.deleteToken().catch(console.error);
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
for (let registration of registrations) {
|
||||
await registration.unregister();
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
return getDeviceToken();
|
||||
} catch (retryError) {
|
||||
console.error('Error during cleanup and retry:', retryError);
|
||||
throw error;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,3 +223,19 @@ messaging.onMessage((payload) => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Automatically initialize FCM when page loads
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Wait a moment for the page to fully initialize
|
||||
setTimeout(() => {
|
||||
getDeviceToken()
|
||||
.then(token => {
|
||||
if (token) {
|
||||
console.log('FCM initialized successfully with token');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Failed to initialize FCM:', error);
|
||||
});
|
||||
}, 2000);
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
// Teacher Modals JavaScript
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('Teacher modals initialized');
|
||||
|
||||
// Initialize any modals that need to be shown
|
||||
const modals = document.querySelectorAll('.modal');
|
||||
modals.forEach(modal => {
|
||||
if (modal.classList.contains('show')) {
|
||||
const modalInstance = new bootstrap.Modal(modal);
|
||||
modalInstance.show();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle modal close events
|
||||
document.querySelectorAll('.modal').forEach(modal => {
|
||||
modal.addEventListener('hidden.bs.modal', function () {
|
||||
// Clean up any form data or reset states
|
||||
const forms = modal.querySelectorAll('form');
|
||||
forms.forEach(form => form.reset());
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user