Getting instance data
This commit is contained in:
Binary file not shown.
@@ -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:
|
||||||
|
|||||||
@@ -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,8 +249,26 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check statuses on page load
|
// Add refresh button to header
|
||||||
checkAllInstanceStatuses();
|
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
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user