// 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(` ${escapeHtml(project.name)} ${escapeHtml(project.jenkinsJob)} ${escapeHtml(project.giteaRepo)} `); }); }) .fail(handleAjaxError); } function loadAPIKeys() { $.get('/api/keys') .done(function(data) { const tbody = $('#apiKeysTable tbody'); tbody.empty(); data.keys.forEach(function(key) { tbody.append(` ${escapeHtml(key.description || 'No description')} ${escapeHtml(key.key)} ${new Date(key.created_at).toLocaleString('zh-CN')} `); }); }) .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(`
${new Date(log.timestamp).toLocaleString('zh-CN')} [${escapeHtml(log.level.toUpperCase())}] ${escapeHtml(log.message)}
`); }); } else { logContainer.append('
No log records
'); } }) .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(`
Status: ${data.status === 'healthy' ? 'Healthy' : 'Unhealthy'}
Version: ${data.version || 'Unknown'}
Uptime: ${data.uptime || 'Unknown'}
Memory Usage: ${data.memory || 'Unknown'}
`); }) .fail(function() { $('#healthDetails').html('
Unable to get health status
'); }); } function loadStatsDetails() { $.get('/api/stats') .done(function(data) { const statsDetails = $('#statsDetails'); statsDetails.html(`
Total Projects: ${data.total_projects || 0}
API Keys: ${data.total_api_keys || 0}
Today's Triggers: ${data.today_triggers || 0}
Successful Triggers: ${data.successful_triggers || 0}
`); }) .fail(function() { $('#statsDetails').html('
Unable to get statistics
'); }); } 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 = $(` `); $('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 = $(` `); $('.main-content').prepend(alert); // Auto dismiss after 3 seconds setTimeout(function() { alert.alert('close'); }, 3000); } function showError(message) { // Create error alert const alert = $(` `); $('.main-content').prepend(alert); // Auto dismiss after 5 seconds setTimeout(function() { alert.alert('close'); }, 5000); } function escapeHtml(unsafe) { return unsafe .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }