268 lines
7.2 KiB
JavaScript
268 lines
7.2 KiB
JavaScript
// 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(`
|
|
<tr>
|
|
<td>${escapeHtml(project.name)}</td>
|
|
<td>${escapeHtml(project.jenkinsJob)}</td>
|
|
<td>${escapeHtml(project.giteaRepo)}</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)}</td>
|
|
<td><code class="api-key">${escapeHtml(
|
|
key.value
|
|
)}</code></td>
|
|
<td>${new Date(key.created).toLocaleString()}</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-danger" onclick="revokeKey('${
|
|
key.id
|
|
}')">
|
|
Revoke
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
`);
|
|
});
|
|
})
|
|
.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(`
|
|
<div class="log-entry ${levelClass}">
|
|
<small>${new Date(log.timestamp).toISOString()}</small>
|
|
[${escapeHtml(log.level)}] ${escapeHtml(log.message)}
|
|
</div>
|
|
`);
|
|
});
|
|
})
|
|
.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, """)
|
|
.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;
|
|
}
|