242 lines
7.6 KiB
JavaScript
242 lines
7.6 KiB
JavaScript
// 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 !== '') {
|
|
const cookies = document.cookie.split(';');
|
|
for (let i = 0; i < cookies.length; i++) {
|
|
const cookie = cookies[i].trim();
|
|
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cookieValue;
|
|
}
|
|
|
|
// Initialize Firebase
|
|
firebase.initializeApp(firebaseConfig);
|
|
const messaging = firebase.messaging();
|
|
|
|
async function registerServiceWorker() {
|
|
try {
|
|
if (!('serviceWorker' in navigator)) {
|
|
throw new Error('Service Worker is not supported in this browser');
|
|
}
|
|
|
|
// Unregister any existing service workers
|
|
const existingRegistrations = await navigator.serviceWorker.getRegistrations();
|
|
for (let registration of existingRegistrations) {
|
|
await registration.unregister();
|
|
}
|
|
|
|
// Register the new service worker
|
|
const registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js', {
|
|
scope: '/',
|
|
updateViaCache: 'none'
|
|
});
|
|
|
|
console.log('Service Worker registered with scope:', registration.scope);
|
|
await navigator.serviceWorker.ready;
|
|
return registration;
|
|
} catch (error) {
|
|
console.error('Service Worker registration failed:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function getDeviceToken() {
|
|
try {
|
|
const registration = await registerServiceWorker();
|
|
|
|
// Request notification permission if not already granted
|
|
let permission = Notification.permission;
|
|
if (permission === 'default') {
|
|
permission = await Notification.requestPermission();
|
|
}
|
|
|
|
if (permission !== 'granted') {
|
|
throw new Error('Notification permission not granted');
|
|
}
|
|
|
|
// Get FCM token
|
|
const token = await messaging.getToken({
|
|
vapidKey: vapidKey,
|
|
serviceWorkerRegistration: registration
|
|
});
|
|
|
|
if (!token) {
|
|
throw new Error('Failed to get FCM token');
|
|
}
|
|
|
|
console.log('Got FCM token:', token);
|
|
|
|
// 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
|
|
}
|
|
} catch (error) {
|
|
console.error('Error in getDeviceToken:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Make getDeviceToken available globally
|
|
window.getDeviceToken = getDeviceToken;
|
|
|
|
// Handle token refresh
|
|
messaging.onTokenRefresh = async () => {
|
|
try {
|
|
await getDeviceToken();
|
|
} catch (error) {
|
|
console.error('Error refreshing token:', error);
|
|
}
|
|
};
|
|
|
|
// Handle foreground messages
|
|
messaging.onMessage((payload) => {
|
|
console.log('Foreground message received:', payload);
|
|
|
|
if (Notification.permission === 'granted') {
|
|
const notificationTitle = payload.notification.title;
|
|
const notificationOptions = {
|
|
body: payload.notification.body,
|
|
icon: payload.notification.icon || '/static/img/logo.png',
|
|
badge: '/static/img/logo.png',
|
|
timestamp: Date.now(),
|
|
requireInteraction: true,
|
|
vibrate: [200, 100, 200],
|
|
tag: 'notification-' + Date.now(),
|
|
data: payload.data || {},
|
|
actions: [
|
|
{
|
|
action: 'open',
|
|
title: 'Open'
|
|
},
|
|
{
|
|
action: 'close',
|
|
title: 'Close'
|
|
}
|
|
]
|
|
};
|
|
|
|
navigator.serviceWorker.ready.then(registration => {
|
|
registration.showNotification(notificationTitle, notificationOptions)
|
|
.catch(error => {
|
|
console.error('Error showing notification:', error);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
// 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);
|
|
});
|