Getting instance data

This commit is contained in:
2025-06-10 09:01:15 +02:00
parent 5c3cce1556
commit 580289d3a1
3 changed files with 292 additions and 14 deletions

View File

@@ -366,7 +366,7 @@ def init_routes(main_bp):
flash('This page is only available in master instances.', 'error') flash('This page is only available in master instances.', 'error')
return redirect(url_for('main.dashboard')) return redirect(url_for('main.dashboard'))
instances = Instance.query.all() instances = Instance.query.order_by(Instance.name.asc()).all()
# Check status for each instance # Check status for each instance
for instance in instances: for instance in instances:

View File

@@ -56,11 +56,20 @@
<td>{{ instance.conversations_count }}</td> <td>{{ instance.conversations_count }}</td>
<td>{{ "%.1f"|format(instance.data_size) }} GB</td> <td>{{ "%.1f"|format(instance.data_size) }} GB</td>
<td>{{ instance.payment_plan }}</td> <td>{{ instance.payment_plan }}</td>
<td>{{ instance.main_url }}</td> <td>
<a href="{{ instance.main_url }}"
target="_blank"
class="text-decoration-none"
style="color: var(--primary-color);">
{{ instance.main_url }}
<i class="fas fa-external-link-alt ms-1"></i>
</a>
</td>
<td> <td>
<span class="badge bg-{{ 'success' if instance.status == 'active' else 'danger' }}" <span class="badge bg-{{ 'success' if instance.status == 'active' else 'danger' }}"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-instance-id="{{ instance.id }}" data-instance-id="{{ instance.id }}"
data-token="{{ instance.connection_token }}"
title="{{ instance.status_details }}"> title="{{ instance.status_details }}">
{{ instance.status|title }} {{ instance.status|title }}
</span> </span>
@@ -240,9 +249,27 @@ document.addEventListener('DOMContentLoaded', function() {
return new bootstrap.Tooltip(tooltipTriggerEl); return new bootstrap.Tooltip(tooltipTriggerEl);
}); });
// Add refresh button to header
const headerButtons = document.querySelector('.header-buttons');
if (headerButtons) {
const refreshButton = document.createElement('button');
refreshButton.className = 'btn btn-outline-primary';
refreshButton.innerHTML = '<i class="fas fa-sync-alt"></i> Refresh';
refreshButton.onclick = function() {
fetchCompanyNames();
};
headerButtons.appendChild(refreshButton);
}
// Wait a short moment to ensure the table is rendered
setTimeout(() => {
// Check statuses on page load // Check statuses on page load
checkAllInstanceStatuses(); checkAllInstanceStatuses();
// Fetch company names for all instances
fetchCompanyNames();
}, 100);
// Set up periodic status checks (every 30 seconds) // Set up periodic status checks (every 30 seconds)
setInterval(checkAllInstanceStatuses, 30000); setInterval(checkAllInstanceStatuses, 30000);
}); });
@@ -295,6 +322,239 @@ async function checkInstanceStatus(instanceId) {
} }
} }
// Function to get JWT token using API key
async function getJWTToken(instanceUrl, apiKey) {
try {
const response = await fetch(`${instanceUrl}/api/admin/management-token`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'X-API-Key': apiKey
}
});
if (!response.ok) {
const errorText = await response.text();
console.error(`HTTP error ${response.status}:`, errorText);
throw new Error(`Server returned ${response.status}: ${errorText}`);
}
const data = await response.json();
return data.token;
} catch (error) {
console.error('Error getting JWT token:', error);
throw error;
}
}
// Function to fetch instance statistics
async function fetchInstanceStats(instanceUrl, instanceId, jwtToken) {
try {
const response = await fetch(`${instanceUrl}/api/admin/statistics`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${jwtToken}`
}
});
if (!response.ok) {
const errorText = await response.text();
console.error(`HTTP error ${response.status}:`, errorText);
throw new Error(`Server returned ${response.status}: ${errorText}`);
}
const data = await response.json();
console.log('Received stats data:', data);
const row = document.querySelector(`[data-instance-id="${instanceId}"]`).closest('tr');
// Update rooms count
const roomsCell = row.querySelector('td:nth-child(3)');
if (roomsCell) {
roomsCell.textContent = data.rooms || '0';
}
// Update conversations count
const conversationsCell = row.querySelector('td:nth-child(4)');
if (conversationsCell) {
conversationsCell.textContent = data.conversations || '0';
}
// Update data usage
const dataCell = row.querySelector('td:nth-child(5)');
if (dataCell) {
const dataSize = data.total_storage || 0;
const dataSizeGB = (dataSize / (1024 * 1024 * 1024)).toFixed(1);
dataCell.textContent = `${dataSizeGB} GB`;
}
return data;
} catch (error) {
console.error('Error fetching instance stats:', error);
throw error;
}
}
// Function to fetch company name from instance settings
async function fetchCompanyName(instanceUrl, instanceId) {
const row = document.querySelector(`[data-instance-id="${instanceId}"]`).closest('tr');
const companyCell = row.querySelector('td:nth-child(2)');
// Show loading state
if (companyCell) {
companyCell.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Loading...';
}
try {
const apiKey = document.querySelector(`[data-instance-id="${instanceId}"]`).dataset.token;
if (!apiKey) {
throw new Error('No API key available');
}
console.log(`Getting JWT token for instance ${instanceId}`);
const jwtToken = await getJWTToken(instanceUrl, apiKey);
console.log('Got JWT token');
// Fetch company name
console.log(`Fetching company name for instance ${instanceId} from ${instanceUrl}/api/admin/settings`);
const response = await fetch(`${instanceUrl}/api/admin/settings`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${jwtToken}`
}
});
if (!response.ok) {
const errorText = await response.text();
console.error(`HTTP error ${response.status}:`, errorText);
throw new Error(`Server returned ${response.status}: ${errorText}`);
}
const data = await response.json();
console.log('Received data:', data);
if (companyCell) {
companyCell.textContent = data.company_name || 'N/A';
}
// Fetch statistics using the same JWT token
await fetchInstanceStats(instanceUrl, instanceId, jwtToken);
} catch (error) {
console.error('Error fetching company name:', error);
if (companyCell) {
const errorMessage = error.message || 'Unknown error';
companyCell.innerHTML = `
<span class="text-danger"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="${errorMessage}">
<i class="fas fa-exclamation-circle"></i> Error
</span>`;
// Initialize tooltip
new bootstrap.Tooltip(companyCell.querySelector('[data-bs-toggle="tooltip"]'));
// Also show error in stats cells
const row = document.querySelector(`[data-instance-id="${instanceId}"]`).closest('tr');
const statsCells = [
row.querySelector('td:nth-child(3)'), // Rooms
row.querySelector('td:nth-child(4)'), // Conversations
row.querySelector('td:nth-child(5)') // Data
];
statsCells.forEach(cell => {
if (cell) {
cell.innerHTML = `
<span class="text-danger"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="${errorMessage}">
<i class="fas fa-exclamation-circle"></i> Error
</span>`;
new bootstrap.Tooltip(cell.querySelector('[data-bs-toggle="tooltip"]'));
}
});
}
}
}
// Function to fetch company names for all instances
async function fetchCompanyNames() {
const instances = document.querySelectorAll('[data-instance-id]');
const loadingPromises = [];
console.log('Starting to fetch company names and stats for all instances');
for (const instance of instances) {
const instanceId = instance.dataset.instanceId;
const row = instance.closest('tr');
// Debug: Log all cells in the row
console.log(`Row for instance ${instanceId}:`, {
cells: Array.from(row.querySelectorAll('td')).map(td => ({
text: td.textContent.trim(),
html: td.innerHTML.trim()
}))
});
// Changed from nth-child(8) to nth-child(7) since Main URL is the 7th column
const urlCell = row.querySelector('td:nth-child(7)');
if (!urlCell) {
console.error(`Could not find URL cell for instance ${instanceId}`);
continue;
}
const urlLink = urlCell.querySelector('a');
if (!urlLink) {
console.error(`Could not find URL link for instance ${instanceId}`);
continue;
}
const instanceUrl = urlLink.getAttribute('href');
const token = instance.dataset.token;
console.log(`Instance ${instanceId}:`, {
url: instanceUrl,
hasToken: !!token
});
if (instanceUrl && token) {
loadingPromises.push(fetchCompanyName(instanceUrl, instanceId));
} else {
const cells = [
row.querySelector('td:nth-child(2)'), // Company
row.querySelector('td:nth-child(3)'), // Rooms
row.querySelector('td:nth-child(4)'), // Conversations
row.querySelector('td:nth-child(5)') // Data
];
cells.forEach(cell => {
if (cell) {
cell.innerHTML = `
<span class="text-warning"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="${!instanceUrl ? 'No URL available' : 'No authentication token available'}">
<i class="fas fa-exclamation-triangle"></i> Not configured
</span>`;
new bootstrap.Tooltip(cell.querySelector('[data-bs-toggle="tooltip"]'));
}
});
}
}
try {
await Promise.all(loadingPromises);
console.log('Finished fetching all company names and stats');
} catch (error) {
console.error('Error in fetchCompanyNames:', error);
}
}
// Show modals // Show modals
function showAddInstanceModal() { function showAddInstanceModal() {
document.getElementById('addInstanceForm').reset(); document.getElementById('addInstanceForm').reset();
@@ -343,19 +603,33 @@ async function submitAddInstance() {
async function editInstance(id) { async function editInstance(id) {
try { try {
const response = await fetch(`/instances/${id}`); // Find the row by looking for the edit button with the matching onclick handler
if (!response.ok) throw new Error('Failed to fetch instance'); const editButton = document.querySelector(`button[onclick="editInstance(${id})"]`);
if (!editButton) {
throw new Error('Instance row not found');
}
const row = editButton.closest('tr');
if (!row) {
throw new Error('Instance row not found');
}
const instance = await response.json(); // Get the name from the first cell
const name = row.querySelector('td:first-child').textContent.trim();
// Get the main URL from the link in the URL cell (7th column)
const urlCell = row.querySelector('td:nth-child(7)');
const urlLink = urlCell.querySelector('a');
const mainUrl = urlLink ? urlLink.getAttribute('href') : urlCell.textContent.trim();
// Populate form // Populate form
document.getElementById('edit_instance_id').value = instance.id; document.getElementById('edit_instance_id').value = id;
document.getElementById('edit_name').value = instance.name; document.getElementById('edit_name').value = name;
document.getElementById('edit_main_url').value = instance.main_url; document.getElementById('edit_main_url').value = mainUrl;
editInstanceModal.show(); editInstanceModal.show();
} catch (error) { } catch (error) {
alert('Error fetching instance: ' + error.message); console.error('Error preparing instance edit:', error);
alert('Error: ' + error.message);
} }
} }
@@ -379,12 +653,16 @@ async function submitEditInstance() {
}) })
}); });
if (!response.ok) throw new Error('Failed to update instance'); if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to update instance: ${errorText}`);
}
editInstanceModal.hide(); editInstanceModal.hide();
location.reload(); // Refresh to show updated instance location.reload(); // Refresh to show updated instance
} catch (error) { } catch (error) {
alert('Error updating instance: ' + error.message); console.error('Error updating instance:', error);
alert('Error: ' + error.message);
} }
} }