Add contact deletion functionality to the API and UI. Implement a new endpoint for deleting contacts, including error handling and logging. Update the contacts management interface to include a delete confirmation modal and handle the deletion process, ensuring proper updates to the displayed contact list and statistics.

This commit is contained in:
agessaman committed 2025-11-30 16:37:35 -08:00
1 parent f40eda8a85
commit b1c39edbb8
2 files changed
+209 -2

No files matched your search

+65
View File
@@ -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"""
+144 -2
View File
@@ -167,7 +167,7 @@
</thead>
<tbody id="contacts-table-body">
<tr>
<td colspan="9" class="text-center">
<td colspan="10" class="text-center">
<div class="loading">Loading contacts data...</div>
</td>
</tr>
@@ -394,7 +394,7 @@ class ModernContactsManager {
'No contacts data available';
tbody.innerHTML = `
<tr>
<td colspan="11" class="text-center text-muted">
<td colspan="10" class="text-center text-muted">
${message}
</td>
</tr>
@@ -434,6 +434,9 @@ class ModernContactsManager {
</button>` :
''
}
<button class="btn btn-sm btn-outline-danger" onclick="contactsManager.showDeleteConfirmation('${contact.user_id.replace(/'/g, "\\'")}', '${(contact.username || 'Unknown').replace(/'/g, "\\'")}')" title="Delete Contact">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
@@ -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 = `
<div class="modal fade" id="deleteContactModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title">
<i class="fas fa-exclamation-triangle"></i> Delete Contact
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p class="mb-3">
<strong>Are you sure you want to delete this contact?</strong>
</p>
<div class="alert alert-warning">
<strong>Contact Information:</strong><br>
<strong>Name:</strong> ${this.escapeHtml(username || 'Unknown')}<br>
<strong>Public Key:</strong> <code>${userId.substring(0, 16)}...</code>
</div>
<div class="alert alert-danger">
<i class="fas fa-exclamation-triangle"></i>
<strong>Warning:</strong> This will permanently delete all tracking data for this contact, including:
<ul class="mb-0 mt-2">
<li>Contact history and statistics</li>
<li>Location information</li>
<li>Advertisement data</li>
<li>Daily statistics</li>
</ul>
<p class="mb-0 mt-2">
<strong>This action cannot be undone.</strong> The contact is not banned and may reappear if heard again.
</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">
<i class="fas fa-trash"></i> Delete Contact
</button>
</div>
</div>
</div>
</div>
`;
// 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 = '<i class="fas fa-spinner fa-spin"></i> 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';