diff --git a/modules/web_viewer/app.py b/modules/web_viewer/app.py index 99bb031..737f69f 100644 --- a/modules/web_viewer/app.py +++ b/modules/web_viewer/app.py @@ -522,6 +522,71 @@ class BotDataViewer: except Exception as e: self.logger.error(f"Error toggling star status: {e}", exc_info=True) return jsonify({'error': str(e)}), 500 + + @self.app.route('/api/delete-contact', methods=['POST']) + def api_delete_contact(): + """Delete a contact from the complete contact tracking database""" + try: + data = request.get_json() + if not data or 'public_key' not in data: + return jsonify({'error': 'public_key is required'}), 400 + + public_key = data['public_key'] + + # Get contact data from database to log what we're deleting + conn = self._get_db_connection() + cursor = conn.cursor() + + # Check if contact exists + cursor.execute(''' + SELECT name, role, device_type FROM complete_contact_tracking + WHERE public_key = ? + ''', (public_key,)) + + contact = cursor.fetchone() + if not contact: + conn.close() + return jsonify({'error': 'Contact not found'}), 404 + + contact_name = contact['name'] + contact_role = contact['role'] + contact_device_type = contact['device_type'] + + # Delete from all related tables + deleted_counts = {} + + # Delete from complete_contact_tracking + cursor.execute('DELETE FROM complete_contact_tracking WHERE public_key = ?', (public_key,)) + deleted_counts['complete_contact_tracking'] = cursor.rowcount + + # Delete from daily_stats + cursor.execute('DELETE FROM daily_stats WHERE public_key = ?', (public_key,)) + deleted_counts['daily_stats'] = cursor.rowcount + + # Delete from repeater_contacts if it exists + try: + cursor.execute('DELETE FROM repeater_contacts WHERE public_key = ?', (public_key,)) + deleted_counts['repeater_contacts'] = cursor.rowcount + except sqlite3.OperationalError: + # Table might not exist, that's okay + deleted_counts['repeater_contacts'] = 0 + + conn.commit() + conn.close() + + # Log the deletion + self.logger.info(f"Contact deleted: {contact_name} ({public_key[:16]}...) - Role: {contact_role}, Device: {contact_device_type}") + self.logger.debug(f"Deleted counts: {deleted_counts}") + + return jsonify({ + 'success': True, + 'message': f'Contact "{contact_name}" has been deleted successfully', + 'deleted_counts': deleted_counts + }) + + except Exception as e: + self.logger.error(f"Error deleting contact: {e}", exc_info=True) + return jsonify({'error': str(e)}), 500 def _setup_socketio_handlers(self): """Setup SocketIO event handlers using modern patterns""" diff --git a/modules/web_viewer/templates/contacts.html b/modules/web_viewer/templates/contacts.html index d8a7741..a6341b7 100644 --- a/modules/web_viewer/templates/contacts.html +++ b/modules/web_viewer/templates/contacts.html @@ -167,7 +167,7 @@ - +
Loading contacts data...
@@ -394,7 +394,7 @@ class ModernContactsManager { 'No contacts data available'; tbody.innerHTML = ` - + ${message} @@ -434,6 +434,9 @@ class ModernContactsManager { ` : '' } + @@ -955,6 +958,145 @@ class ModernContactsManager { } } + showDeleteConfirmation(userId, username) { + // Find the contact data + const contact = this.filteredData.find(c => c.user_id === userId); + if (!contact) { + this.showError('Contact data not found'); + return; + } + + // Create a modal for delete confirmation + const modalHtml = ` + + `; + + // Remove existing modal if any + const existingModal = document.getElementById('deleteContactModal'); + if (existingModal) { + existingModal.remove(); + } + + // Add modal to page + document.body.insertAdjacentHTML('beforeend', modalHtml); + + // Get the modal element + const modalElement = document.getElementById('deleteContactModal'); + const modal = new bootstrap.Modal(modalElement); + + // Set up delete button handler + const confirmBtn = document.getElementById('confirmDeleteBtn'); + confirmBtn.addEventListener('click', () => { + this.deleteContact(userId, username, modalElement); + }); + + // Show modal + modal.show(); + + // Clean up modal when hidden + modalElement.addEventListener('hidden.bs.modal', () => { + modalElement.remove(); + }); + } + + async deleteContact(userId, username, modalElement) { + // Disable the delete button and show loading state + const confirmBtn = document.getElementById('confirmDeleteBtn'); + const originalHTML = confirmBtn.innerHTML; + confirmBtn.disabled = true; + confirmBtn.innerHTML = ' Deleting...'; + + try { + // Call the delete API + const response = await fetch('/api/delete-contact', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + public_key: userId + }) + }); + + const data = await response.json(); + + if (!response.ok || !data.success) { + throw new Error(data.error || 'Failed to delete contact'); + } + + // Close the modal + const modal = bootstrap.Modal.getInstance(modalElement); + if (modal) { + modal.hide(); + } + + // Remove contact from local data + this.contactsData.tracking_data = this.contactsData.tracking_data.filter(c => c.user_id !== userId); + this.filteredData = this.filteredData.filter(c => c.user_id !== userId); + + // Re-render the table + this.renderContactsData(); + this.updateStatistics(); + + // Show success message + this.showSuccess(`Contact "${username || 'Unknown'}" has been deleted successfully.`); + + } catch (error) { + console.error('Error deleting contact:', error); + this.showError('Failed to delete contact: ' + error.message); + + // Restore button state + confirmBtn.disabled = false; + confirmBtn.innerHTML = originalHTML; + } + } + + escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + showError(message) { const errorDiv = document.createElement('div'); errorDiv.className = 'alert alert-danger alert-dismissible fade show';