Spaces:
Running
Running
// management/static/js/main.js | |
// -*- coding: utf-8 -*- | |
// VAPID_PUBLIC_KEY will be passed from the Django template. | |
// This is a placeholder; it will be filled with the correct value in the HTML file. | |
const VAPID_PUBLIC_KEY_PLACEHOLDER = "{{ vapid_public_key }}"; | |
/** | |
* Converts a base64 string to a Uint8Array. | |
* @param {string} base64String | |
* @returns {Uint8Array} | |
*/ | |
function urlBase64ToUint8Array(base64String) { | |
const padding = '='.repeat((4 - base64String.length % 4) % 4); | |
const base64 = (base64String + padding) | |
.replace(/\-/g, '+') | |
.replace(/_/g, '/'); | |
const rawData = window.atob(base64); | |
const outputArray = new Uint8Array(rawData.length); | |
for (let i = 0; i < rawData.length; ++i) { | |
outputArray[i] = rawData.charCodeAt(i); | |
} | |
return outputArray; | |
} | |
/** | |
* Registers the service worker for the PWA. | |
* @returns {Promise<ServiceWorkerRegistration|null>} | |
*/ | |
async function registerServiceWorker() { | |
if ('serviceWorker' in navigator) { | |
try { | |
const registration = await navigator.serviceWorker.register('/service-worker.js'); | |
console.log('Service worker registered successfully.'); | |
return registration; | |
} catch (error) { | |
console.error('Service worker registration failed:', error); | |
return null; | |
} | |
} | |
console.error('Service workers are not supported in this browser.'); | |
return null; | |
} | |
/** | |
* Subscribes the user to push notifications. | |
* @param {ServiceWorkerRegistration} registration | |
*/ | |
async function subscribeUserToPush(registration) { | |
if (!('PushManager' in window)) { | |
console.error('Push notifications are not supported.'); | |
return; | |
} | |
if (!VAPID_PUBLIC_KEY_PLACEHOLDER || VAPID_PUBLIC_KEY_PLACEHOLDER === "YOUR_VAPID_PUBLIC_KEY_HERE") { | |
console.error("VAPID public key not configured."); | |
return; | |
} | |
try { | |
const subscription = await registration.pushManager.subscribe({ | |
userVisibleOnly: true, | |
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY_PLACEHOLDER) | |
}); | |
console.log('User is subscribed to push.'); | |
const response = await fetch('/api/push/subscribe/', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value | |
}, | |
body: JSON.stringify(subscription) | |
}); | |
if (!response.ok) { | |
console.error('Failed to save push subscription on server.'); | |
} | |
} catch (error) { | |
console.error('Push subscription failed:', error); | |
} | |
} | |
/** | |
* Fetches and displays user dashboard data from the API. | |
*/ | |
async function fetchDashboardData() { | |
try { | |
const response = await fetch('/api/dashboard/'); | |
if (!response.ok) { | |
throw new Error('Network response was not ok'); | |
} | |
const data = await response.json(); | |
document.getElementById('username-display').textContent = data.username; | |
document.getElementById('service-status').textContent = data.service_status; | |
document.getElementById('used-traffic').textContent = (data.used_traffic / (1024 ** 3)).toFixed(2) + ' GB'; | |
document.getElementById('total-traffic').textContent = (data.total_traffic / (1024 ** 3)).toFixed(2) + ' GB'; | |
const configsList = document.getElementById('configs-list'); | |
configsList.innerHTML = ''; // Clear previous content | |
data.configs.forEach(config => { | |
const listItem = document.createElement('li'); | |
listItem.className = 'bg-gray-50 rounded-md p-3 border border-gray-200'; | |
listItem.innerHTML = ` | |
<div class="font-bold text-gray-800">${config.remark}</div> | |
<div class="text-sm text-gray-500 break-all">${config.link}</div> | |
`; | |
configsList.appendChild(listItem); | |
}); | |
} catch (error) { | |
console.error('Error fetching dashboard data:', error); | |
document.body.innerHTML = '<p class="text-red-500 text-center">خطا در بارگذاری اطلاعات داشبورد.</p>'; | |
} | |
} | |
/** | |
* Handles the generation of the Telegram security token. | |
*/ | |
async function handleTelegramTokenGeneration() { | |
const generateTokenBtn = document.getElementById('generate-token-btn'); | |
const tokenDisplayArea = document.getElementById('token-display-area'); | |
const telegramTokenCode = document.getElementById('telegram-token'); | |
if (!generateTokenBtn) return; | |
generateTokenBtn.addEventListener('click', async () => { | |
const response = await fetch('/api/telegram/generate-token/', { | |
method: 'POST', | |
headers: { | |
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value | |
} | |
}); | |
if (response.ok) { | |
const data = await response.json(); | |
telegramTokenCode.textContent = data.token; | |
tokenDisplayArea.classList.remove('hidden'); | |
} else { | |
// Using a custom modal or message box instead of alert() | |
console.error('Failed to generate token.'); | |
alert('خطا در تولید توکن.'); | |
} | |
}); | |
} | |
document.addEventListener('DOMContentLoaded', async () => { | |
// Fetch and render dashboard data | |
await fetchDashboardData(); | |
// PWA and Push Notification logic | |
const registration = await registerServiceWorker(); | |
if (registration) { | |
if (Notification.permission === 'granted') { | |
console.log('Push permission already granted.'); | |
} else if (Notification.permission === 'denied') { | |
console.log('Push permission denied by user.'); | |
} else { | |
// Prompt the user for permission | |
// This is a browser feature, so we don't need a custom modal | |
Notification.requestPermission().then(permission => { | |
if (permission === 'granted') { | |
subscribeUserToPush(registration); | |
} else { | |
console.log('User denied push notification permission.'); | |
} | |
}); | |
} | |
} | |
// Telegram token generation | |
handleTelegramTokenGeneration(); | |
}); | |