274 lines
8.1 KiB
JavaScript
274 lines
8.1 KiB
JavaScript
const API_URL = ""; // Relative path since served by same origin
|
|
|
|
// State
|
|
let token = localStorage.getItem("token");
|
|
let currentUser = null;
|
|
|
|
// DOM Elements
|
|
const loginPage = document.getElementById("login-page");
|
|
const dashboardLayout = document.getElementById("dashboard-layout");
|
|
const loginForm = document.getElementById("login-form");
|
|
const loginError = document.getElementById("login-error");
|
|
const logoutBtn = document.getElementById("logout-btn");
|
|
const usersSection = document.getElementById("users-section");
|
|
const appsSection = document.getElementById("apps-section");
|
|
const usersTableBody = document.querySelector("#users-table tbody");
|
|
const appsTableBody = document.querySelector("#apps-table tbody");
|
|
|
|
// Init
|
|
function init() {
|
|
if (token) {
|
|
showDashboard();
|
|
} else {
|
|
showLogin();
|
|
}
|
|
}
|
|
|
|
// Navigation
|
|
function showLogin() {
|
|
loginPage.classList.remove("hidden");
|
|
dashboardLayout.classList.add("hidden");
|
|
}
|
|
|
|
function showDashboard() {
|
|
loginPage.classList.add("hidden");
|
|
dashboardLayout.classList.remove("hidden");
|
|
loadUsers();
|
|
}
|
|
|
|
function showSection(section) {
|
|
if (section === 'users') {
|
|
usersSection.classList.remove("hidden");
|
|
appsSection.classList.add("hidden");
|
|
loadUsers();
|
|
} else {
|
|
usersSection.classList.add("hidden");
|
|
appsSection.classList.remove("hidden");
|
|
loadApps();
|
|
}
|
|
}
|
|
|
|
// Auth
|
|
loginForm.addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
const username = document.getElementById("username").value;
|
|
const password = document.getElementById("password").value;
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append("username", username);
|
|
formData.append("password", password);
|
|
|
|
const res = await fetch(`${API_URL}/token`, {
|
|
method: "POST",
|
|
body: formData
|
|
});
|
|
|
|
if (!res.ok) throw new Error("Invalid credentials");
|
|
|
|
const data = await res.json();
|
|
token = data.access_token;
|
|
localStorage.setItem("token", token);
|
|
loginError.classList.add("hidden");
|
|
showDashboard();
|
|
} catch (err) {
|
|
loginError.textContent = err.message;
|
|
loginError.classList.remove("hidden");
|
|
}
|
|
});
|
|
|
|
logoutBtn.addEventListener("click", () => {
|
|
token = null;
|
|
localStorage.removeItem("token");
|
|
showLogin();
|
|
});
|
|
|
|
// API Helpers
|
|
async function authFetch(url, options = {}) {
|
|
const headers = {
|
|
...options.headers,
|
|
"Authorization": `Bearer ${token}`
|
|
};
|
|
|
|
const res = await fetch(url, { ...options, headers });
|
|
if (res.status === 401) {
|
|
logoutBtn.click();
|
|
throw new Error("Unauthorized");
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// Users
|
|
async function loadUsers() {
|
|
const res = await authFetch(`${API_URL}/users/`);
|
|
const users = await res.json();
|
|
usersTableBody.innerHTML = users.map(user => `
|
|
<tr>
|
|
<td>${user.id}</td>
|
|
<td>${user.username}</td>
|
|
<td>${user.email}</td>
|
|
<td>${user.is_admin ? "Yes" : "No"}</td>
|
|
<td>${user.is_active ? "Yes" : "No"}</td>
|
|
<td>
|
|
<button class="btn btn-secondary" onclick='editUser(${JSON.stringify(user)})'>Edit</button>
|
|
<button class="btn btn-primary" onclick='assignAppModal(${user.id})'>Assign App</button>
|
|
</td>
|
|
</tr>
|
|
`).join("");
|
|
}
|
|
|
|
// Apps
|
|
async function loadApps() {
|
|
const res = await authFetch(`${API_URL}/apps/`);
|
|
const apps = await res.json();
|
|
appsTableBody.innerHTML = apps.map(app => `
|
|
<tr>
|
|
<td>${app.id}</td>
|
|
<td>${app.name}</td>
|
|
<td>${app.url}</td>
|
|
<td><code>${app.api_key}</code></td>
|
|
</tr>
|
|
`).join("");
|
|
}
|
|
|
|
// Modals
|
|
function openModal(id) {
|
|
document.getElementById(id).classList.remove("hidden");
|
|
}
|
|
|
|
function closeModal(id) {
|
|
document.getElementById(id).classList.add("hidden");
|
|
}
|
|
|
|
// User Form
|
|
const userForm = document.getElementById("user-form");
|
|
userForm.addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
const id = document.getElementById("user-id").value;
|
|
const username = document.getElementById("user-username").value;
|
|
const email = document.getElementById("user-email").value;
|
|
const password = document.getElementById("user-password").value;
|
|
const isAdmin = document.getElementById("user-is-admin").checked;
|
|
const isActive = document.getElementById("user-is-active").checked;
|
|
|
|
const data = { username, email, is_admin: isAdmin, is_active: isActive };
|
|
if (password) data.password = password;
|
|
|
|
try {
|
|
let res;
|
|
if (id) {
|
|
res = await authFetch(`${API_URL}/users/${id}`, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(data)
|
|
});
|
|
} else {
|
|
if (!password) return alert("Password required for new user");
|
|
res = await authFetch(`${API_URL}/users/`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(data)
|
|
});
|
|
}
|
|
|
|
if (!res.ok) {
|
|
const err = await res.json();
|
|
throw new Error(err.detail);
|
|
}
|
|
|
|
closeModal("user-modal");
|
|
loadUsers();
|
|
} catch (err) {
|
|
alert(err.message);
|
|
}
|
|
});
|
|
|
|
window.addUser = () => {
|
|
document.getElementById("user-id").value = "";
|
|
document.getElementById("user-username").value = "";
|
|
document.getElementById("user-email").value = "";
|
|
document.getElementById("user-password").value = "";
|
|
document.getElementById("user-is-admin").checked = false;
|
|
document.getElementById("user-is-active").checked = true;
|
|
openModal("user-modal");
|
|
};
|
|
|
|
window.addApp = () => {
|
|
document.getElementById("app-name").value = "";
|
|
document.getElementById("app-url").value = "";
|
|
openModal("app-modal");
|
|
};
|
|
|
|
window.editUser = (user) => {
|
|
document.getElementById("user-id").value = user.id;
|
|
document.getElementById("user-username").value = user.username;
|
|
document.getElementById("user-email").value = user.email;
|
|
document.getElementById("user-password").value = "";
|
|
document.getElementById("user-is-admin").checked = user.is_admin;
|
|
document.getElementById("user-is-active").checked = user.is_active;
|
|
openModal("user-modal");
|
|
};
|
|
|
|
// App Form
|
|
const appForm = document.getElementById("app-form");
|
|
appForm.addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
const name = document.getElementById("app-name").value;
|
|
const url = document.getElementById("app-url").value;
|
|
|
|
try {
|
|
const res = await authFetch(`${API_URL}/apps/`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ name, url })
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const err = await res.json();
|
|
throw new Error(err.detail);
|
|
}
|
|
|
|
closeModal("app-modal");
|
|
loadApps();
|
|
} catch (err) {
|
|
alert(err.message);
|
|
}
|
|
});
|
|
|
|
// Assign App
|
|
window.assignAppModal = async (userId) => {
|
|
document.getElementById("assign-user-id").value = userId;
|
|
|
|
// Load apps for select
|
|
const res = await authFetch(`${API_URL}/apps/`);
|
|
const apps = await res.json();
|
|
const select = document.getElementById("assign-app-select");
|
|
select.innerHTML = '<option value="">Select Application</option>' +
|
|
apps.map(app => `<option value="${app.id}">${app.name}</option>`).join("");
|
|
|
|
openModal("assign-modal");
|
|
};
|
|
|
|
const assignForm = document.getElementById("assign-form");
|
|
assignForm.addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
const userId = document.getElementById("assign-user-id").value;
|
|
const appId = document.getElementById("assign-app-select").value;
|
|
|
|
try {
|
|
const res = await authFetch(`${API_URL}/users/${userId}/assign/${appId}`, {
|
|
method: "POST"
|
|
});
|
|
|
|
if (!res.ok) throw new Error("Failed to assign");
|
|
|
|
closeModal("assign-modal");
|
|
alert("Assigned successfully");
|
|
} catch (err) {
|
|
alert(err.message);
|
|
}
|
|
});
|
|
|
|
// Start
|
|
init();
|