mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-10-23 06:12:19 +00:00
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:
1 parent
f40eda8a85
commit
b1c39edbb8
2 files changed
+209
-2
No files matched your search
@@ -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"""
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in new issue
Block a user