freeleaps-ops/apps/gitea-webhook-ambassador-python/app/static/js/dashboard.js

373 lines
12 KiB
JavaScript

// Global variable to store JWT token
let authToken = localStorage.getItem('auth_token');
$(document).ready(function() {
// Check authentication status
if (!authToken) {
window.location.href = '/login';
return;
}
// Set AJAX default config
$.ajaxSetup({
beforeSend: function(xhr, settings) {
// Do not add auth header for login request
if (settings.url === '/api/auth/login') {
return;
}
if (authToken) {
xhr.setRequestHeader('Authorization', 'Bearer ' + authToken);
}
},
error: function(xhr, status, error) {
// If 401 received, redirect to login page
if (xhr.status === 401) {
localStorage.removeItem('auth_token');
window.location.href = '/login';
return;
}
handleAjaxError(xhr, status, error);
}
});
// Initialize tooltips
$('[data-bs-toggle="tooltip"]').tooltip();
// Load initial data
loadProjects();
loadAPIKeys();
loadLogs();
checkHealth();
loadHealthDetails();
loadStatsDetails();
// Set periodic health check
setInterval(checkHealth, 30000);
// Project management
$('#addProjectForm').on('submit', function(e) {
e.preventDefault();
const projectData = {
name: $('#projectName').val(),
jenkinsJob: $('#jenkinsJob').val(),
giteaRepo: $('#giteaRepo').val()
};
$.ajax({
url: '/api/projects/',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify(projectData),
success: function() {
$('#addProjectModal').modal('hide');
$('#addProjectForm')[0].reset();
loadProjects();
showSuccess('Project added successfully');
},
error: handleAjaxError
});
});
// API key management
$('#generateKeyForm').on('submit', function(e) {
e.preventDefault();
$.ajax({
url: '/api/keys',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ description: $('#keyDescription').val() }),
success: function(response) {
$('#generateKeyModal').modal('hide');
$('#generateKeyForm')[0].reset();
loadAPIKeys();
showSuccess('API key generated successfully');
// Show newly generated key
showApiKeyModal(response.key);
},
error: handleAjaxError
});
});
// Log query
$('#logQueryForm').on('submit', function(e) {
e.preventDefault();
loadLogs({
startTime: $('#startTime').val(),
endTime: $('#endTime').val(),
level: $('#logLevel').val(),
query: $('#logQuery').val()
});
});
// Tab switching
$('.nav-link').on('click', function() {
$('.nav-link').removeClass('active');
$(this).addClass('active');
});
});
function loadProjects() {
$.get('/api/projects/')
.done(function(data) {
const tbody = $('#projectsTable tbody');
tbody.empty();
data.projects.forEach(function(project) {
tbody.append(`
<tr>
<td>${escapeHtml(project.name)}</td>
<td>${escapeHtml(project.jenkinsJob)}</td>
<td>${escapeHtml(project.giteaRepo)}</td>
<td>
<button class="btn btn-sm btn-danger" onclick="deleteProject(${project.id})">
<i class="bi bi-trash"></i> Delete
</button>
</td>
</tr>
`);
});
})
.fail(handleAjaxError);
}
function loadAPIKeys() {
$.get('/api/keys')
.done(function(data) {
const tbody = $('#apiKeysTable tbody');
tbody.empty();
data.keys.forEach(function(key) {
tbody.append(`
<tr>
<td>${escapeHtml(key.description || 'No description')}</td>
<td><code class="api-key">${escapeHtml(key.key)}</code></td>
<td>${new Date(key.created_at).toLocaleString('zh-CN')}</td>
<td>
<button class="btn btn-sm btn-danger" onclick="revokeKey(${key.id})">
<i class="bi bi-trash"></i> Revoke
</button>
</td>
</tr>
`);
});
})
.fail(handleAjaxError);
}
function loadLogs(query = {}) {
$.get('/api/logs', query)
.done(function(data) {
const logContainer = $('#logEntries');
logContainer.empty();
if (data.logs && data.logs.length > 0) {
data.logs.forEach(function(log) {
const levelClass = {
'error': 'error',
'warn': 'warn',
'info': 'info',
'debug': 'debug'
}[log.level] || '';
logContainer.append(`
<div class="log-entry ${levelClass}">
<small>${new Date(log.timestamp).toLocaleString('zh-CN')}</small>
[${escapeHtml(log.level.toUpperCase())}] ${escapeHtml(log.message)}
</div>
`);
});
} else {
logContainer.append('<div class="text-muted">No log records</div>');
}
})
.fail(handleAjaxError);
}
function checkHealth() {
$.get('/health')
.done(function(data) {
const indicator = $('.health-indicator');
indicator.removeClass('healthy unhealthy')
.addClass(data.status === 'healthy' ? 'healthy' : 'unhealthy');
$('#healthStatus').text(data.status === 'healthy' ? 'Healthy' : 'Unhealthy');
})
.fail(function() {
const indicator = $('.health-indicator');
indicator.removeClass('healthy').addClass('unhealthy');
$('#healthStatus').text('Unhealthy');
});
}
function loadHealthDetails() {
$.get('/health')
.done(function(data) {
const healthDetails = $('#healthDetails');
healthDetails.html(`
<div class="mb-3">
<strong>Status:</strong>
<span class="badge ${data.status === 'healthy' ? 'bg-success' : 'bg-danger'}">
${data.status === 'healthy' ? 'Healthy' : 'Unhealthy'}
</span>
</div>
<div class="mb-3">
<strong>Version:</strong> ${data.version || 'Unknown'}
</div>
<div class="mb-3">
<strong>Uptime:</strong> ${data.uptime || 'Unknown'}
</div>
<div class="mb-3">
<strong>Memory Usage:</strong> ${data.memory || 'Unknown'}
</div>
`);
})
.fail(function() {
$('#healthDetails').html('<div class="text-danger">Unable to get health status</div>');
});
}
function loadStatsDetails() {
$.get('/api/stats')
.done(function(data) {
const statsDetails = $('#statsDetails');
statsDetails.html(`
<div class="mb-3">
<strong>Total Projects:</strong> ${data.total_projects || 0}
</div>
<div class="mb-3">
<strong>API Keys:</strong> ${data.total_api_keys || 0}
</div>
<div class="mb-3">
<strong>Today's Triggers:</strong> ${data.today_triggers || 0}
</div>
<div class="mb-3">
<strong>Successful Triggers:</strong> ${data.successful_triggers || 0}
</div>
`);
})
.fail(function() {
$('#statsDetails').html('<div class="text-danger">Unable to get statistics</div>');
});
}
function deleteProject(id) {
if (!confirm('Are you sure you want to delete this project?')) return;
$.ajax({
url: `/api/projects/${id}`,
method: 'DELETE',
success: function() {
loadProjects();
showSuccess('Project deleted successfully');
},
error: handleAjaxError
});
}
function revokeKey(id) {
if (!confirm('Are you sure you want to revoke this API key?')) return;
$.ajax({
url: `/api/keys/${id}`,
method: 'DELETE',
success: function() {
loadAPIKeys();
showSuccess('API key revoked successfully');
},
error: handleAjaxError
});
}
function showApiKeyModal(key) {
// Create modal to show newly generated key
const modal = $(`
<div class="modal fade" id="newApiKeyModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">New API Key</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="alert alert-warning">
<strong>Important:</strong> Please save this key, as it will only be shown once!
</div>
<div class="mb-3">
<label class="form-label">API Key:</label>
<input type="text" class="form-control" value="${key}" readonly>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="copyToClipboard('${key}')">
Copy to Clipboard
</button>
</div>
</div>
</div>
</div>
`);
$('body').append(modal);
modal.modal('show');
modal.on('hidden.bs.modal', function() {
modal.remove();
});
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(function() {
showSuccess('Copied to clipboard');
}, function() {
showError('Copy failed');
});
}
function handleAjaxError(jqXHR, textStatus, errorThrown) {
const message = jqXHR.responseJSON?.detail || errorThrown || 'An error occurred';
showError(`Error: ${message}`);
}
function showSuccess(message) {
// Create success alert
const alert = $(`
<div class="alert alert-success alert-dismissible fade show" role="alert">
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
`);
$('.main-content').prepend(alert);
// Auto dismiss after 3 seconds
setTimeout(function() {
alert.alert('close');
}, 3000);
}
function showError(message) {
// Create error alert
const alert = $(`
<div class="alert alert-danger alert-dismissible fade show" role="alert">
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
`);
$('.main-content').prepend(alert);
// Auto dismiss after 5 seconds
setTimeout(function() {
alert.alert('close');
}, 5000);
}
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}