// Global variable to store the JWT token let authToken = localStorage.getItem("auth_token"); $(document).ready(function () { // Initialize tooltips $('[data-bs-toggle="tooltip"]').tooltip(); // Set up AJAX defaults to include auth token $.ajaxSetup({ beforeSend: function (xhr, settings) { // Don't 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 we get a 401, redirect to login if (xhr.status === 401) { localStorage.removeItem("auth_token"); window.location.href = "/login"; return; } handleAjaxError(xhr, status, error); }, }); // Handle login form submission $("#loginForm").on("submit", function (e) { e.preventDefault(); const secretKey = $("#secret_key").val(); $("#loginError").hide(); $.ajax({ url: "/api/auth/login", method: "POST", contentType: "application/json", data: JSON.stringify({ secret_key: secretKey }), success: function (response) { if (response && response.token) { // Store token and redirect localStorage.setItem("auth_token", response.token); authToken = response.token; window.location.href = "/dashboard"; } else { $("#loginError").text("Invalid response from server").show(); } }, error: function (xhr) { console.error("Login error:", xhr); if (xhr.responseJSON && xhr.responseJSON.error) { $("#loginError").text(xhr.responseJSON.error).show(); } else { $("#loginError").text("Login failed. Please try again.").show(); } $("#secret_key").val("").focus(); }, }); }); // Only load dashboard data if we're on the dashboard page if (window.location.pathname === "/dashboard") { if (!authToken) { window.location.href = "/login"; return; } // Load initial data loadProjects(); loadAPIKeys(); loadLogs(); checkHealth(); // Set up 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"); loadProjects(); }, 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 () { $("#generateKeyModal").modal("hide"); loadAPIKeys(); }, error: handleAjaxError, }); }); // Log querying $("#logQueryForm").on("submit", function (e) { e.preventDefault(); loadLogs({ startTime: $("#startTime").val(), endTime: $("#endTime").val(), level: $("#logLevel").val(), query: $("#logQuery").val(), }); }); }); 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)} ${escapeHtml( key.value )} ${new Date(key.created).toLocaleString()} `); }); }) .fail(handleAjaxError); } function loadLogs(query = {}) { $.get("/api/logs", query) .done(function (data) { const logContainer = $("#logEntries"); logContainer.empty(); data.logs.forEach(function (log) { const levelClass = { error: "text-danger", warn: "text-warning", info: "text-info", debug: "text-secondary", }[log.level] || ""; logContainer.append(`
${new Date(log.timestamp).toISOString()} [${escapeHtml(log.level)}] ${escapeHtml(log.message)}
`); }); }) .fail(handleAjaxError); } function checkHealth() { $.get("/api/health") .done(function (data) { const indicator = $(".health-indicator"); indicator .removeClass("healthy unhealthy") .addClass(data.status === "healthy" ? "healthy" : "unhealthy"); $("#healthStatus").text(data.status); }) .fail(function () { const indicator = $(".health-indicator"); indicator.removeClass("healthy").addClass("unhealthy"); $("#healthStatus").text("unhealthy"); }); } function deleteProject(id) { if (!confirm("Are you sure you want to delete this project?")) return; $.ajax({ url: `/api/projects/${id}`, method: "DELETE", success: loadProjects, 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: loadAPIKeys, error: handleAjaxError, }); } function handleAjaxError(jqXHR, textStatus, errorThrown) { const message = jqXHR.responseJSON?.error || errorThrown || "An error occurred"; alert(`Error: ${message}`); } function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function getCookie(name) { const cookies = document.cookie.split(";"); for (let cookie of cookies) { const [cookieName, cookieValue] = cookie.split("=").map((c) => c.trim()); if (cookieName === name) { console.debug(`Found cookie ${name}`); return cookieValue; } } console.debug(`Cookie ${name} not found`); return null; }