| 
 | 1 | +// Dashboard functionality for DirectAdmin Email Forwarder  | 
 | 2 | +let currentForwarders = [];  | 
 | 3 | +let emailAccounts = [];  | 
 | 4 | + | 
 | 5 | +// Load email accounts for destination dropdown  | 
 | 6 | +async function loadEmailAccounts() {  | 
 | 7 | +    try {  | 
 | 8 | +        const response = await fetch('/api/email-accounts');  | 
 | 9 | +        const data = await response.json();  | 
 | 10 | + | 
 | 11 | +        if (response.ok && data.accounts) {  | 
 | 12 | +            emailAccounts = data.accounts;  | 
 | 13 | +            updateDestinationDropdown();  | 
 | 14 | +        } else {  | 
 | 15 | +            console.error('Failed to load email accounts:', data.error);  | 
 | 16 | +            showMessage('Failed to load email accounts', 'error');  | 
 | 17 | +        }  | 
 | 18 | +    } catch (error) {  | 
 | 19 | +        console.error('Error loading email accounts:', error);  | 
 | 20 | +    }  | 
 | 21 | +}  | 
 | 22 | + | 
 | 23 | +// Update destination dropdown with email accounts  | 
 | 24 | +function updateDestinationDropdown() {  | 
 | 25 | +    const select = document.getElementById('destination');  | 
 | 26 | +    if (!select) return;  | 
 | 27 | + | 
 | 28 | +    // Clear existing options  | 
 | 29 | +    select.innerHTML = '';  | 
 | 30 | + | 
 | 31 | +    // Add placeholder  | 
 | 32 | +    const placeholder = document.createElement('option');  | 
 | 33 | +    placeholder.value = '';  | 
 | 34 | +    placeholder.textContent = 'Select destination...';  | 
 | 35 | +    placeholder.disabled = true;  | 
 | 36 | +    placeholder.selected = true;  | 
 | 37 | +    select.appendChild(placeholder);  | 
 | 38 | + | 
 | 39 | +    // Add email account options  | 
 | 40 | +    if (emailAccounts.length > 0) {  | 
 | 41 | +        emailAccounts.forEach(email => {  | 
 | 42 | +            const option = document.createElement('option');  | 
 | 43 | +            option.value = email;  | 
 | 44 | +            option.textContent = email;  | 
 | 45 | +            select.appendChild(option);  | 
 | 46 | +        });  | 
 | 47 | +    }  | 
 | 48 | + | 
 | 49 | +    // Add separator  | 
 | 50 | +    const separator = document.createElement('option');  | 
 | 51 | +    separator.disabled = true;  | 
 | 52 | +    separator.textContent = '──────────────';  | 
 | 53 | +    select.appendChild(separator);  | 
 | 54 | + | 
 | 55 | +    // Add custom email option  | 
 | 56 | +    const customOption = document.createElement('option');  | 
 | 57 | +    customOption.value = 'custom';  | 
 | 58 | +    customOption.textContent = 'Enter custom email address...';  | 
 | 59 | +    select.appendChild(customOption);  | 
 | 60 | + | 
 | 61 | +    // Handle selection changes  | 
 | 62 | +    select.addEventListener('change', function() {  | 
 | 63 | +        const customInput = document.getElementById('customDestination');  | 
 | 64 | +        if (this.value === 'custom') {  | 
 | 65 | +            customInput.style.display = 'block';  | 
 | 66 | +            customInput.required = true;  | 
 | 67 | +            customInput.focus();  | 
 | 68 | +        } else {  | 
 | 69 | +            customInput.style.display = 'none';  | 
 | 70 | +            customInput.required = false;  | 
 | 71 | +            customInput.value = '';  | 
 | 72 | +        }  | 
 | 73 | +    });  | 
 | 74 | +}  | 
 | 75 | + | 
 | 76 | +// Load forwarders from API  | 
 | 77 | +async function loadForwarders() {  | 
 | 78 | +    const tbody = document.querySelector('#forwardersTable tbody');  | 
 | 79 | +    if (!tbody) return;  | 
 | 80 | + | 
 | 81 | +    try {  | 
 | 82 | +        const response = await fetch('/api/forwarders');  | 
 | 83 | + | 
 | 84 | +        if (!response.ok) {  | 
 | 85 | +            throw new Error(`HTTP error! status: ${response.status}`);  | 
 | 86 | +        }  | 
 | 87 | + | 
 | 88 | +        const data = await response.json();  | 
 | 89 | +        console.log('Forwarders response:', data);  | 
 | 90 | + | 
 | 91 | +        // Extract forwarders array from response  | 
 | 92 | +        if (data && Array.isArray(data.forwarders)) {  | 
 | 93 | +            currentForwarders = data.forwarders;  | 
 | 94 | +        } else {  | 
 | 95 | +            console.warn('Unexpected forwarders format:', data);  | 
 | 96 | +            currentForwarders = [];  | 
 | 97 | +        }  | 
 | 98 | + | 
 | 99 | +        displayForwarders();  | 
 | 100 | + | 
 | 101 | +    } catch (error) {  | 
 | 102 | +        console.error('Error loading forwarders:', error);  | 
 | 103 | +        tbody.innerHTML = '<tr><td colspan="3" class="error-message">Failed to load forwarders. Please check your DirectAdmin settings.</td></tr>';  | 
 | 104 | +    }  | 
 | 105 | +}  | 
 | 106 | + | 
 | 107 | +// Display forwarders in the table  | 
 | 108 | +function displayForwarders() {  | 
 | 109 | +    const tbody = document.querySelector('#forwardersTable tbody');  | 
 | 110 | +    if (!tbody) return;  | 
 | 111 | + | 
 | 112 | +    tbody.innerHTML = '';  | 
 | 113 | + | 
 | 114 | +    if (currentForwarders.length === 0) {  | 
 | 115 | +        tbody.innerHTML = '<tr><td colspan="3" class="no-data">No email forwarders configured</td></tr>';  | 
 | 116 | +        return;  | 
 | 117 | +    }  | 
 | 118 | + | 
 | 119 | +    currentForwarders.forEach(forwarder => {  | 
 | 120 | +        const row = document.createElement('tr');  | 
 | 121 | + | 
 | 122 | +        // Create cells  | 
 | 123 | +        const fromCell = document.createElement('td');  | 
 | 124 | +        fromCell.textContent = forwarder.address || 'Unknown';  | 
 | 125 | + | 
 | 126 | +        const toCell = document.createElement('td');  | 
 | 127 | +        toCell.textContent = forwarder.destination || 'Unknown';  | 
 | 128 | + | 
 | 129 | +        const actionsCell = document.createElement('td');  | 
 | 130 | +        const deleteBtn = document.createElement('button');  | 
 | 131 | +        deleteBtn.className = 'btn-danger btn-sm';  | 
 | 132 | +        deleteBtn.textContent = 'Delete';  | 
 | 133 | +        deleteBtn.onclick = () => deleteForwarder(forwarder.address);  | 
 | 134 | +        actionsCell.appendChild(deleteBtn);  | 
 | 135 | + | 
 | 136 | +        row.appendChild(fromCell);  | 
 | 137 | +        row.appendChild(toCell);  | 
 | 138 | +        row.appendChild(actionsCell);  | 
 | 139 | +        tbody.appendChild(row);  | 
 | 140 | +    });  | 
 | 141 | +}  | 
 | 142 | + | 
 | 143 | +// Create new forwarder  | 
 | 144 | +async function createForwarder(event) {  | 
 | 145 | +    event.preventDefault();  | 
 | 146 | + | 
 | 147 | +    const form = event.target;  | 
 | 148 | +    const addressInput = form.querySelector('#address');  | 
 | 149 | +    const destinationSelect = form.querySelector('#destination');  | 
 | 150 | +    const customDestInput = form.querySelector('#customDestination');  | 
 | 151 | +    const submitButton = form.querySelector('button[type="submit"]');  | 
 | 152 | + | 
 | 153 | +    // Get the actual destination  | 
 | 154 | +    let destination = destinationSelect.value;  | 
 | 155 | +    if (destination === 'custom') {  | 
 | 156 | +        destination = customDestInput.value.trim();  | 
 | 157 | +        if (!destination) {  | 
 | 158 | +            showMessage('Please enter a custom email address', 'error');  | 
 | 159 | +            customDestInput.focus();  | 
 | 160 | +            return;  | 
 | 161 | +        }  | 
 | 162 | +    }  | 
 | 163 | + | 
 | 164 | +    // Validate  | 
 | 165 | +    if (!addressInput.value.trim() || !destination) {  | 
 | 166 | +        showMessage('Please fill in all required fields', 'error');  | 
 | 167 | +        return;  | 
 | 168 | +    }  | 
 | 169 | + | 
 | 170 | +    // Disable form during submission  | 
 | 171 | +    submitButton.disabled = true;  | 
 | 172 | +    submitButton.textContent = 'Creating...';  | 
 | 173 | + | 
 | 174 | +    try {  | 
 | 175 | +        const response = await fetch('/api/forwarders', {  | 
 | 176 | +            method: 'POST',  | 
 | 177 | +            headers: {  | 
 | 178 | +                'Content-Type': 'application/json',  | 
 | 179 | +            },  | 
 | 180 | +            body: JSON.stringify({  | 
 | 181 | +                address: addressInput.value.trim(),  | 
 | 182 | +                destination: destination  | 
 | 183 | +            })  | 
 | 184 | +        });  | 
 | 185 | + | 
 | 186 | +        const data = await response.json();  | 
 | 187 | + | 
 | 188 | +        if (response.ok) {  | 
 | 189 | +            // Clear form  | 
 | 190 | +            form.reset();  | 
 | 191 | +            customDestInput.style.display = 'none';  | 
 | 192 | + | 
 | 193 | +            // Reload forwarders  | 
 | 194 | +            await loadForwarders();  | 
 | 195 | + | 
 | 196 | +            // Show success message  | 
 | 197 | +            showMessage(data.message || 'Forwarder created successfully', 'success');  | 
 | 198 | +        } else {  | 
 | 199 | +            showMessage(data.error || 'Failed to create forwarder', 'error');  | 
 | 200 | +        }  | 
 | 201 | +    } catch (error) {  | 
 | 202 | +        console.error('Error creating forwarder:', error);  | 
 | 203 | +        showMessage('Failed to create forwarder. Please try again.', 'error');  | 
 | 204 | +    } finally {  | 
 | 205 | +        // Re-enable form  | 
 | 206 | +        submitButton.disabled = false;  | 
 | 207 | +        submitButton.textContent = 'Create Forwarder';  | 
 | 208 | +    }  | 
 | 209 | +}  | 
 | 210 | + | 
 | 211 | +// Delete forwarder  | 
 | 212 | +async function deleteForwarder(address) {  | 
 | 213 | +    if (!address) {  | 
 | 214 | +        console.error('No address provided for deletion');  | 
 | 215 | +        return;  | 
 | 216 | +    }  | 
 | 217 | + | 
 | 218 | +    if (!confirm(`Are you sure you want to delete the forwarder for ${address}?`)) {  | 
 | 219 | +        return;  | 
 | 220 | +    }  | 
 | 221 | + | 
 | 222 | +    try {  | 
 | 223 | +        const response = await fetch('/api/forwarders', {  | 
 | 224 | +            method: 'DELETE',  | 
 | 225 | +            headers: {  | 
 | 226 | +                'Content-Type': 'application/json',  | 
 | 227 | +            },  | 
 | 228 | +            body: JSON.stringify({  | 
 | 229 | +                address: address  | 
 | 230 | +            })  | 
 | 231 | +        });  | 
 | 232 | + | 
 | 233 | +        const data = await response.json();  | 
 | 234 | + | 
 | 235 | +        if (response.ok) {  | 
 | 236 | +            await loadForwarders();  | 
 | 237 | +            showMessage(data.message || 'Forwarder deleted successfully', 'success');  | 
 | 238 | +        } else {  | 
 | 239 | +            showMessage(data.error || 'Failed to delete forwarder', 'error');  | 
 | 240 | +        }  | 
 | 241 | +    } catch (error) {  | 
 | 242 | +        console.error('Error deleting forwarder:', error);  | 
 | 243 | +        showMessage('Failed to delete forwarder. Please try again.', 'error');  | 
 | 244 | +    }  | 
 | 245 | +}  | 
 | 246 | + | 
 | 247 | +// Show message to user  | 
 | 248 | +function showMessage(message, type = 'info') {  | 
 | 249 | +    const container = document.getElementById('messageContainer');  | 
 | 250 | +    if (!container) {  | 
 | 251 | +        console.error('Message container not found');  | 
 | 252 | +        return;  | 
 | 253 | +    }  | 
 | 254 | + | 
 | 255 | +    // Create message element  | 
 | 256 | +    const div = document.createElement('div');  | 
 | 257 | +    div.className = `alert alert-${type}`;  | 
 | 258 | +    div.textContent = message;  | 
 | 259 | + | 
 | 260 | +    // Add close button  | 
 | 261 | +    const closeBtn = document.createElement('span');  | 
 | 262 | +    closeBtn.innerHTML = '×';  | 
 | 263 | +    closeBtn.style.float = 'right';  | 
 | 264 | +    closeBtn.style.cursor = 'pointer';  | 
 | 265 | +    closeBtn.style.marginLeft = '15px';  | 
 | 266 | +    closeBtn.onclick = () => div.remove();  | 
 | 267 | +    div.appendChild(closeBtn);  | 
 | 268 | + | 
 | 269 | +    // Clear existing messages and add new one  | 
 | 270 | +    container.innerHTML = '';  | 
 | 271 | +    container.appendChild(div);  | 
 | 272 | + | 
 | 273 | +    // Auto-hide after 5 seconds  | 
 | 274 | +    setTimeout(() => {  | 
 | 275 | +        if (div.parentNode) {  | 
 | 276 | +            div.remove();  | 
 | 277 | +        }  | 
 | 278 | +    }, 5000);  | 
 | 279 | +}  | 
 | 280 | + | 
 | 281 | +// Initialize when DOM is ready  | 
 | 282 | +document.addEventListener('DOMContentLoaded', function() {  | 
 | 283 | +    console.log('Dashboard JS loaded');  | 
 | 284 | + | 
 | 285 | +    // Check if we're on the dashboard page  | 
 | 286 | +    const forwardersTable = document.getElementById('forwardersTable');  | 
 | 287 | +    if (!forwardersTable) {  | 
 | 288 | +        console.log('Not on dashboard page, skipping initialization');  | 
 | 289 | +        return;  | 
 | 290 | +    }  | 
 | 291 | + | 
 | 292 | +    console.log('Initializing dashboard...');  | 
 | 293 | + | 
 | 294 | +    // Load initial data  | 
 | 295 | +    loadEmailAccounts();  | 
 | 296 | +    loadForwarders();  | 
 | 297 | + | 
 | 298 | +    // Set up auto-refresh every 60 seconds  | 
 | 299 | +    setInterval(loadForwarders, 60000);  | 
 | 300 | + | 
 | 301 | +    // Set up form handler  | 
 | 302 | +    const form = document.getElementById('createForwarderForm');  | 
 | 303 | +    if (form) {  | 
 | 304 | +        form.addEventListener('submit', createForwarder);  | 
 | 305 | +    } else {  | 
 | 306 | +        console.error('Create forwarder form not found');  | 
 | 307 | +    }  | 
 | 308 | + | 
 | 309 | +    // Set up manual refresh button (if you want one)  | 
 | 310 | +    const refreshBtn = document.getElementById('refreshBtn');  | 
 | 311 | +    if (refreshBtn) {  | 
 | 312 | +        refreshBtn.addEventListener('click', () => {  | 
 | 313 | +            loadForwarders();  | 
 | 314 | +            showMessage('Forwarders refreshed', 'info');  | 
 | 315 | +        });  | 
 | 316 | +    }  | 
 | 317 | +});  | 
 | 318 | + | 
 | 319 | +// Utility function to escape HTML (prevent XSS)  | 
 | 320 | +function escapeHtml(unsafe) {  | 
 | 321 | +    if (!unsafe) return '';  | 
 | 322 | +    return unsafe  | 
 | 323 | +        .toString()  | 
 | 324 | +        .replace(/&/g, "&")  | 
 | 325 | +        .replace(/</g, "<")  | 
 | 326 | +        .replace(/>/g, ">")  | 
 | 327 | +        .replace(/"/g, """)  | 
 | 328 | +        .replace(/'/g, "'");  | 
 | 329 | +}  | 
0 commit comments