@@ -1,272 +0,0 @@
|
||||
/**
|
||||
* @fileoverview Manages the events page functionality.
|
||||
* This file handles event filtering, pagination, and modal interactions.
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Elements
|
||||
const eventTypeFilter = document.getElementById('eventTypeFilter');
|
||||
const dateRangeFilter = document.getElementById('dateRangeFilter');
|
||||
const userFilter = document.getElementById('userFilter');
|
||||
const applyFiltersBtn = document.getElementById('applyFilters');
|
||||
const eventsTableBody = document.getElementById('eventsTableBody');
|
||||
const prevPageBtn = document.getElementById('prevPage');
|
||||
const nextPageBtn = document.getElementById('nextPage');
|
||||
const currentPageSpan = document.getElementById('currentPage');
|
||||
const totalPagesSpan = document.getElementById('totalPages');
|
||||
const eventDetailsModal = document.getElementById('eventDetailsModal');
|
||||
const eventDetailsContent = document.getElementById('eventDetailsContent');
|
||||
|
||||
// State
|
||||
let currentPage = 1;
|
||||
let totalPages = 1;
|
||||
let currentFilters = {
|
||||
eventType: '',
|
||||
dateRange: '24h',
|
||||
userId: ''
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads events based on current filters and page
|
||||
*/
|
||||
async function loadEvents() {
|
||||
try {
|
||||
const response = await fetch(`/api/events?page=${currentPage}&${new URLSearchParams(currentFilters)}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
renderEvents(data.events);
|
||||
updatePagination(data.total_pages);
|
||||
} else {
|
||||
console.error('Failed to load events:', data.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading events:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders events in the table
|
||||
*/
|
||||
function renderEvents(events) {
|
||||
if (!Array.isArray(events)) {
|
||||
console.error('Invalid events data received:', events);
|
||||
return;
|
||||
}
|
||||
|
||||
const eventRows = events.map(event => {
|
||||
if (!event || typeof event !== 'object') {
|
||||
console.error('Invalid event data:', event);
|
||||
return '';
|
||||
}
|
||||
|
||||
// Determine badge color and text based on event type
|
||||
let badgeClass = 'bg-secondary';
|
||||
let eventText = event.event_type || 'Unknown Event';
|
||||
|
||||
switch(event.event_type) {
|
||||
case 'user_login':
|
||||
badgeClass = 'bg-success';
|
||||
eventText = 'User Login';
|
||||
break;
|
||||
case 'user_logout':
|
||||
badgeClass = 'bg-secondary';
|
||||
eventText = 'User Logout';
|
||||
break;
|
||||
case 'user_register':
|
||||
badgeClass = 'bg-info';
|
||||
eventText = 'User Registration';
|
||||
break;
|
||||
case 'user_update':
|
||||
badgeClass = 'bg-primary';
|
||||
eventText = 'User Update';
|
||||
break;
|
||||
case 'file_upload':
|
||||
badgeClass = 'bg-success';
|
||||
eventText = 'File Upload';
|
||||
break;
|
||||
case 'file_delete':
|
||||
badgeClass = 'bg-danger';
|
||||
eventText = 'File Delete';
|
||||
break;
|
||||
case 'file_download':
|
||||
badgeClass = 'bg-info';
|
||||
eventText = 'File Download';
|
||||
break;
|
||||
case 'file_restore':
|
||||
badgeClass = 'bg-warning';
|
||||
eventText = 'File Restore';
|
||||
break;
|
||||
case 'file_move':
|
||||
badgeClass = 'bg-primary';
|
||||
eventText = 'File Move';
|
||||
break;
|
||||
case 'file_rename':
|
||||
badgeClass = 'bg-info';
|
||||
eventText = 'File Rename';
|
||||
break;
|
||||
case 'file_star':
|
||||
badgeClass = 'bg-warning';
|
||||
eventText = 'File Star';
|
||||
break;
|
||||
case 'file_unstar':
|
||||
badgeClass = 'bg-secondary';
|
||||
eventText = 'File Unstar';
|
||||
break;
|
||||
case 'room_create':
|
||||
badgeClass = 'bg-success';
|
||||
eventText = 'Room Create';
|
||||
break;
|
||||
case 'room_delete':
|
||||
badgeClass = 'bg-danger';
|
||||
eventText = 'Room Delete';
|
||||
break;
|
||||
case 'room_update':
|
||||
badgeClass = 'bg-primary';
|
||||
eventText = 'Room Update';
|
||||
break;
|
||||
case 'room_join':
|
||||
badgeClass = 'bg-info';
|
||||
eventText = 'Room Join';
|
||||
break;
|
||||
case 'room_leave':
|
||||
badgeClass = 'bg-secondary';
|
||||
eventText = 'Room Leave';
|
||||
break;
|
||||
case 'conversation_create':
|
||||
badgeClass = 'bg-success';
|
||||
eventText = 'Conversation Create';
|
||||
break;
|
||||
case 'conversation_delete':
|
||||
badgeClass = 'bg-danger';
|
||||
eventText = 'Conversation Delete';
|
||||
break;
|
||||
case 'message_sent':
|
||||
badgeClass = 'bg-primary';
|
||||
eventText = 'Message Sent';
|
||||
break;
|
||||
case 'attachment_download':
|
||||
badgeClass = 'bg-info';
|
||||
eventText = 'Attachment Download';
|
||||
break;
|
||||
}
|
||||
|
||||
const timestamp = event.timestamp ? new Date(event.timestamp).toLocaleString() : '-';
|
||||
const username = event.user?.username || 'Unknown User';
|
||||
const lastName = event.user?.last_name || '';
|
||||
const eventId = event.id || '';
|
||||
const ipAddress = event.ip_address || '-';
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>${timestamp}</td>
|
||||
<td><span class="badge ${badgeClass}">${eventText}</span></td>
|
||||
<td>${username} ${lastName}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-secondary"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#eventDetailsModal"
|
||||
data-event-id="${eventId}">
|
||||
<i class="fas fa-info-circle"></i> View Details
|
||||
</button>
|
||||
</td>
|
||||
<td>${ipAddress}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
eventsTableBody.innerHTML = eventRows;
|
||||
|
||||
// Add event listeners to detail buttons
|
||||
document.querySelectorAll('[data-event-id]').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const eventId = button.dataset.eventId;
|
||||
if (eventId) {
|
||||
showEventDetails(eventId);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates pagination controls
|
||||
*/
|
||||
function updatePagination(total) {
|
||||
totalPages = total;
|
||||
currentPageSpan.textContent = currentPage;
|
||||
totalPagesSpan.textContent = totalPages;
|
||||
|
||||
prevPageBtn.classList.toggle('disabled', currentPage === 1);
|
||||
nextPageBtn.classList.toggle('disabled', currentPage === totalPages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows event details in modal
|
||||
*/
|
||||
async function showEventDetails(eventId) {
|
||||
try {
|
||||
const response = await fetch(`/api/events/${eventId}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
const event = data.event;
|
||||
eventDetailsContent.textContent = JSON.stringify(event.details, null, 2);
|
||||
} else {
|
||||
console.error('Failed to load event details:', data.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading event details:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads users for the user filter
|
||||
*/
|
||||
async function loadUsers() {
|
||||
try {
|
||||
const response = await fetch('/api/users');
|
||||
const data = await response.json();
|
||||
|
||||
const userFilter = document.getElementById('userFilter');
|
||||
userFilter.innerHTML = '<option value="">All Users</option>';
|
||||
|
||||
data.users.forEach(user => {
|
||||
const option = document.createElement('option');
|
||||
option.value = user.id;
|
||||
option.textContent = `${user.username} ${user.last_name || ''}`.trim();
|
||||
userFilter.appendChild(option);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading users:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
applyFiltersBtn.addEventListener('click', () => {
|
||||
currentFilters = {
|
||||
eventType: eventTypeFilter.value,
|
||||
dateRange: dateRangeFilter.value,
|
||||
userId: userFilter.value
|
||||
};
|
||||
currentPage = 1;
|
||||
loadEvents();
|
||||
});
|
||||
|
||||
prevPageBtn.addEventListener('click', () => {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
loadEvents();
|
||||
}
|
||||
});
|
||||
|
||||
nextPageBtn.addEventListener('click', () => {
|
||||
if (currentPage < totalPages) {
|
||||
currentPage++;
|
||||
loadEvents();
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize
|
||||
loadUsers();
|
||||
loadEvents();
|
||||
});
|
||||
@@ -354,24 +354,6 @@ function toggleStar(filename, path = '', roomId) {
|
||||
.then(r => r.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
// Log the star/unstar event
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: res.starred ? 'file_star' : 'file_unstar',
|
||||
details: {
|
||||
filename: filename,
|
||||
path: path,
|
||||
room_id: roomId,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Remove the file from the current view since it's no longer starred
|
||||
currentFiles = currentFiles.filter(f => !(f.name === filename && f.path === path && f.room_id === roomId));
|
||||
renderFiles(currentFiles);
|
||||
@@ -412,24 +394,6 @@ function restoreFile(filename, path = '', roomId) {
|
||||
.then(r => r.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
// Log the restore event
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'file_restore',
|
||||
details: {
|
||||
filename: filename,
|
||||
path: path,
|
||||
room_id: roomId,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Remove the file from the current view since it's been restored
|
||||
currentFiles = currentFiles.filter(f => !(f.name === filename && f.path === path && f.room_id === roomId));
|
||||
renderFiles(currentFiles);
|
||||
@@ -470,7 +434,7 @@ function permanentDeleteFile() {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/api/rooms/${roomId}/delete_permanent`, {
|
||||
fetch(`/api/rooms/${roomId}/delete-permanent`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -481,40 +445,39 @@ function permanentDeleteFile() {
|
||||
path: path
|
||||
})
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
// Check if the response is empty
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
return response.json();
|
||||
}
|
||||
return { success: true }; // If no JSON response, assume success
|
||||
})
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
// Log the permanent delete event
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'file_delete_permanent',
|
||||
details: {
|
||||
filename: filename,
|
||||
path: path,
|
||||
room_id: roomId,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Remove the file from the current view
|
||||
// Remove the file from the current view since it's been deleted
|
||||
currentFiles = currentFiles.filter(f => !(f.name === filename && f.path === path && f.room_id === roomId));
|
||||
renderFiles(currentFiles);
|
||||
|
||||
// Close the modal
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('permanentDeleteModal'));
|
||||
modal.hide();
|
||||
if (modal) {
|
||||
modal.hide();
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to delete file permanently:', res.error);
|
||||
console.error('Failed to delete file:', res.error || 'Unknown error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error deleting file permanently:', error);
|
||||
console.error('Error deleting file:', error);
|
||||
// Show error to user
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('permanentDeleteModal'));
|
||||
if (modal) {
|
||||
modal.hide();
|
||||
}
|
||||
// You might want to show an error message to the user here
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,95 +13,19 @@
|
||||
* - Auto-save functionality for permission changes
|
||||
* @function
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Initialize Select2
|
||||
$(document).ready(function() {
|
||||
// Initialize Select2 for user selection
|
||||
$('.select2').select2({
|
||||
theme: 'bootstrap-5'
|
||||
theme: 'bootstrap-5',
|
||||
width: '100%',
|
||||
placeholder: 'Search for a user...',
|
||||
allowClear: true
|
||||
});
|
||||
|
||||
// Log when a member is added to the room
|
||||
const addMemberForm = document.querySelector('form[action*="/add_member"]');
|
||||
if (addMemberForm) {
|
||||
addMemberForm.addEventListener('submit', function(e) {
|
||||
const formData = new FormData(this);
|
||||
const userId = formData.get('user_id');
|
||||
const roomId = this.action.split('/rooms/')[1].split('/add_member')[0];
|
||||
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'room_join',
|
||||
details: {
|
||||
room_id: roomId,
|
||||
user_id: userId,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Log when a member is removed from the room
|
||||
const removeMemberForms = document.querySelectorAll('form[action*="/remove_member"]');
|
||||
removeMemberForms.forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
const roomId = this.action.split('/rooms/')[1].split('/remove_member')[0];
|
||||
const userId = this.action.split('/users/')[1];
|
||||
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'room_leave',
|
||||
details: {
|
||||
room_id: roomId,
|
||||
user_id: userId,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Log when member permissions are updated
|
||||
const permissionForms = document.querySelectorAll('.auto-save-perms-form');
|
||||
permissionForms.forEach(form => {
|
||||
form.addEventListener('change', function(e) {
|
||||
const roomId = this.action.split('/rooms/')[1].split('/update_member_permissions')[0];
|
||||
const userId = this.action.split('/users/')[1];
|
||||
const formData = new FormData(this);
|
||||
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'room_member_permissions_update',
|
||||
details: {
|
||||
room_id: roomId,
|
||||
user_id: userId,
|
||||
permissions: {
|
||||
can_view: formData.get('can_view') === '1',
|
||||
can_download: formData.get('can_download') === 'on',
|
||||
can_upload: formData.get('can_upload') === 'on',
|
||||
can_delete: formData.get('can_delete') === 'on',
|
||||
can_rename: formData.get('can_rename') === 'on',
|
||||
can_move: formData.get('can_move') === 'on',
|
||||
can_share: formData.get('can_share') === 'on'
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Auto-submit permission form on checkbox change
|
||||
document.querySelectorAll('.auto-save-perms-form input[type="checkbox"]').forEach(function(checkbox) {
|
||||
checkbox.addEventListener('change', function() {
|
||||
this.closest('form').submit();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -46,137 +46,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
form.submit();
|
||||
});
|
||||
}
|
||||
|
||||
// Log when rooms page is viewed
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'room_list_view',
|
||||
details: {
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Log when a room is opened
|
||||
const openRoomButtons = document.querySelectorAll('a[href*="/room/"]');
|
||||
openRoomButtons.forEach(button => {
|
||||
button.addEventListener('click', function(e) {
|
||||
const roomId = this.href.split('/room/')[1];
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'room_open',
|
||||
details: {
|
||||
room_id: roomId,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Log when a room is deleted
|
||||
const deleteRoomForms = document.querySelectorAll('form[action*="/delete"]');
|
||||
deleteRoomForms.forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
const roomId = this.action.split('/rooms/')[1].split('/delete')[0];
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'room_delete',
|
||||
details: {
|
||||
room_id: roomId,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Log when a room is created
|
||||
const createRoomForm = document.querySelector('form[action*="/create"]');
|
||||
if (createRoomForm) {
|
||||
createRoomForm.addEventListener('submit', function(e) {
|
||||
const formData = new FormData(this);
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'room_create',
|
||||
details: {
|
||||
room_name: formData.get('name'),
|
||||
description: formData.get('description'),
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Log when a room is edited
|
||||
const editRoomForm = document.querySelector('form[action*="/edit"]');
|
||||
if (editRoomForm) {
|
||||
editRoomForm.addEventListener('submit', function(e) {
|
||||
const roomId = this.action.split('/rooms/')[1].split('/edit')[0];
|
||||
const formData = new FormData(this);
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'room_update',
|
||||
details: {
|
||||
room_id: roomId,
|
||||
room_name: formData.get('name'),
|
||||
description: formData.get('description'),
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Log when room search is performed
|
||||
if (searchInput) {
|
||||
let searchTimeout;
|
||||
searchInput.addEventListener('input', function(e) {
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => {
|
||||
if (this.value.length > 0) {
|
||||
fetch('/api/events/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: 'room_search',
|
||||
details: {
|
||||
search_term: this.value,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user