// Software Upgrade System - Frontend Functions // Requires: jQuery or modern fetch API class UpgradeManager { constructor(apiBase = '/api.php') { this.apiBase = apiBase; this.serviceToken = ''; this.init(); } init() { // Get service token from DOM if available const tokenElement = document.getElementById('servicetoken'); if (tokenElement) { this.serviceToken = tokenElement.innerHTML || ''; } } async makeAPICall(endpoint, method = 'GET', data = null) { const url = this.apiBase + endpoint; const bearer = 'Bearer ' + this.serviceToken; const options = { method: method, headers: { 'Authorization': bearer, 'Content-Type': 'application/json' }, credentials: 'include' }; if (data && (method === 'POST' || method === 'PUT')) { options.body = JSON.stringify(data); } const response = await fetch(url, options); if (!response.ok) { const errorData = await response.json().catch(() => ({ error: 'Network error' })); throw new Error(errorData.error || `HTTP ${response.status}`); } return await response.json(); } async getAvailableVersions() { try { const data = await this.makeAPICall('/v2/get/software?available'); return data; } catch (error) { console.error('Error fetching available versions:', error); throw error; } } async downloadVersion(versionId, onProgress = null) { try { // Step 1: Request download token const downloadRequest = await this.makeAPICall('/v2/post/software', 'POST', { action: 'download', version_id: parseInt(versionId) }); if (!downloadRequest.download_url) { throw new Error('No download URL received'); } // Step 2: Download file using temporary URL await this.downloadFile(downloadRequest.download_url, onProgress); } catch (error) { console.error('Download error:', error); throw error; } } async downloadFile(url, onProgress) { const response = await fetch(url, { credentials: 'include' }); if (!response.ok) { throw new Error('Download failed'); } const contentLength = response.headers.get('Content-Length'); const total = parseInt(contentLength, 10); let loaded = 0; const reader = response.body.getReader(); const chunks = []; while (true) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); loaded += value.length; if (onProgress && total) { onProgress(loaded, total); } } // Create blob from chunks const blob = new Blob(chunks); // Trigger download const downloadUrl = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = downloadUrl; a.download = 'software_upgrade.zip'; // Filename will be set by server document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(downloadUrl); document.body.removeChild(a); } async purchaseVersion(versionId, transactionId = null) { try { const purchaseData = { action: 'purchase', version_id: parseInt(versionId) }; if (transactionId) { purchaseData.transaction_id = transactionId; } const result = await this.makeAPICall('/v2/post/software', 'POST', purchaseData); return result; } catch (error) { console.error('Purchase error:', error); throw error; } } formatBytes(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; } formatPrice(price, currency = 'USD') { return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency }).format(price); } } // Global upgrade manager instance let upgradeManager; // Initialize upgrade system function initUpgradeSystem() { upgradeManager = new UpgradeManager(); } // Display upgrade options in UI async function showUpgradeOptions(containerId = 'upgrade-container') { const container = document.getElementById(containerId); if (!container) { console.error('Container element not found:', containerId); return; } try { const data = await upgradeManager.getAvailableVersions(); container.innerHTML = ''; // Show current version info if (data.current_version) { const currentDiv = document.createElement('div'); currentDiv.className = 'current-version-info'; currentDiv.innerHTML = `

Your Current Version: ${data.current_version}

Owned versions: ${data.owned_versions.map(v => v.version).join(', ')}

`; container.appendChild(currentDiv); } // Show available versions if (data.available_versions && data.available_versions.length > 0) { const versionsDiv = document.createElement('div'); versionsDiv.className = 'available-versions'; data.available_versions.forEach(version => { const versionCard = document.createElement('div'); versionCard.className = 'version-card'; versionCard.dataset.versionId = version.id; let buttonHTML = ''; let priceHTML = ''; let statusHTML = ''; if (version.is_accessible) { statusHTML = 'Owned'; buttonHTML = ``; } else if (version.requires_payment) { if (version.is_upgrade) { priceHTML = `
${upgradeManager.formatPrice(version.price)} ${upgradeManager.formatPrice(version.original_price)} Upgrade from v${version.upgrade_from}
`; } else { priceHTML = `
${upgradeManager.formatPrice(version.price)}
`; } buttonHTML = ``; } else if (version.access_reason === 'requires_base_version') { statusHTML = `Requires v${version.required_version}`; buttonHTML = ``; } versionCard.innerHTML = `

${version.name} ${statusHTML}

v${version.version}
${version.description}
Size: ${upgradeManager.formatBytes(version.file_size)} Released: ${new Date(version.release_date).toLocaleDateString()}
${priceHTML}
${buttonHTML}
`; versionsDiv.appendChild(versionCard); }); container.appendChild(versionsDiv); } else { container.innerHTML = '

No software versions available at this time.

'; } } catch (error) { container.innerHTML = `
Error loading upgrades: ${error.message}
`; console.error('Error showing upgrade options:', error); } } // Download version with progress async function downloadVersion(versionId) { const button = event.target; const originalText = button.innerHTML; try { button.disabled = true; button.innerHTML = 'Preparing Download...'; // Create progress indicator const progressContainer = document.createElement('div'); progressContainer.className = 'download-progress'; progressContainer.innerHTML = `
0%
`; button.parentNode.appendChild(progressContainer); const progressFill = progressContainer.querySelector('.progress-fill'); const progressText = progressContainer.querySelector('.progress-text'); await upgradeManager.downloadVersion(versionId, (loaded, total) => { const percent = Math.round((loaded / total) * 100); progressFill.style.width = percent + '%'; progressText.textContent = percent + '%'; }); button.innerHTML = 'Download Complete!'; progressText.textContent = 'Complete'; // Remove progress after a delay setTimeout(() => { progressContainer.remove(); button.innerHTML = originalText; button.disabled = false; }, 3000); } catch (error) { button.innerHTML = 'Download Failed'; button.disabled = false; alert('Download failed: ' + error.message); // Remove progress on error const progressContainer = button.parentNode.querySelector('.download-progress'); if (progressContainer) { progressContainer.remove(); } } } // Purchase version async function purchaseVersion(versionId, price) { const button = event.target; const originalText = button.innerHTML; const confirmed = confirm(`Purchase this software version for ${upgradeManager.formatPrice(price)}?`); if (!confirmed) return; try { button.disabled = true; button.innerHTML = 'Processing Purchase...'; // Here you would integrate with your payment processor // For now, we'll simulate with a transaction ID const transactionId = 'txn_' + Date.now(); const result = await upgradeManager.purchaseVersion(versionId, transactionId); if (result.success) { button.innerHTML = 'Purchase Successful!'; button.className = 'success-btn'; // Refresh the upgrade options setTimeout(() => { showUpgradeOptions(); }, 2000); } else { throw new Error(result.error || 'Purchase failed'); } } catch (error) { button.innerHTML = 'Purchase Failed'; button.disabled = false; alert('Purchase failed: ' + error.message); } } // Initialize when DOM is ready document.addEventListener('DOMContentLoaded', function() { initUpgradeSystem(); }); // Export for module usage (optional) if (typeof module !== 'undefined' && module.exports) { module.exports = { UpgradeManager, upgradeManager }; }