179 lines
5.6 KiB
JavaScript
179 lines
5.6 KiB
JavaScript
(() => {
|
|
const pluginList = document.querySelector('#plugin-list');
|
|
const userList = document.querySelector('#user-list');
|
|
const userForm = document.querySelector('#user-form');
|
|
const adminControls = document.querySelector('#admin-controls');
|
|
const adminAuthNotice = document.querySelector('#admin-auth-notice');
|
|
const accessToken = localStorage.getItem('linklogAccessToken');
|
|
|
|
function authHeaders(includeJson = false) {
|
|
return {
|
|
...(includeJson ? {'Content-Type': 'application/json'} : {}),
|
|
...(accessToken ? {Authorization: `Bearer ${accessToken}`} : {}),
|
|
};
|
|
}
|
|
|
|
function renderPlugins(plugins) {
|
|
pluginList.replaceChildren(...plugins.map((plugin) => {
|
|
const row = document.createElement('div');
|
|
row.className = 'plugin-row';
|
|
|
|
const label = document.createElement('span');
|
|
label.textContent = `${plugin.name} (${plugin.version})`;
|
|
|
|
const button = document.createElement('button');
|
|
button.type = 'button';
|
|
button.textContent = plugin.enabled ? 'Disable' : 'Enable';
|
|
button.addEventListener('click', () => updatePlugin(plugin, button));
|
|
|
|
row.append(label, button);
|
|
return row;
|
|
}));
|
|
}
|
|
|
|
function renderUsers(users) {
|
|
userList.replaceChildren(...users.map((user) => {
|
|
const row = document.createElement('div');
|
|
row.className = 'plugin-row';
|
|
const label = document.createElement('span');
|
|
label.textContent = `${user.username} (${user.email})${user.is_admin ? ' - admin' : ''}`;
|
|
const privilegeLabel = document.createElement('label');
|
|
privilegeLabel.className = 'user-admin-toggle';
|
|
const privilegeCheckbox = document.createElement('input');
|
|
privilegeCheckbox.type = 'checkbox';
|
|
privilegeCheckbox.checked = user.is_admin;
|
|
privilegeCheckbox.setAttribute('aria-label', `Administrator rights for ${user.username}`);
|
|
privilegeCheckbox.addEventListener('change', () => updateUserPrivilege(user, privilegeCheckbox));
|
|
privilegeLabel.append(privilegeCheckbox, document.createTextNode(' Administrator'));
|
|
const button = document.createElement('button');
|
|
button.type = 'button';
|
|
button.className = 'danger-button';
|
|
button.textContent = 'Remove';
|
|
button.addEventListener('click', () => removeUser(user, button));
|
|
row.append(label, privilegeLabel, button);
|
|
return row;
|
|
}));
|
|
}
|
|
|
|
async function loadUsers() {
|
|
const response = await fetch('/api/admin/users', {headers: authHeaders()});
|
|
if (!response.ok) throw new Error('Could not load users');
|
|
renderUsers(await response.json());
|
|
}
|
|
|
|
function showAdminState(isAdmin) {
|
|
adminControls.classList.toggle('hidden', !isAdmin);
|
|
adminAuthNotice.classList.toggle('hidden', isAdmin);
|
|
}
|
|
|
|
function showSignedOutState() {
|
|
showAdminState(false);
|
|
adminAuthNotice.innerHTML = 'Administrator sign-in required. <a href="/login">Sign in</a>';
|
|
}
|
|
|
|
function showUnauthorizedState() {
|
|
showAdminState(false);
|
|
adminAuthNotice.textContent = 'You are signed in, but you are not authorized to access this page.';
|
|
}
|
|
|
|
async function loadAdminState() {
|
|
if (!accessToken) {
|
|
showSignedOutState();
|
|
return;
|
|
}
|
|
|
|
const sessionResponse = await fetch(`/api/auth/me?token=${encodeURIComponent(accessToken)}`);
|
|
if (!sessionResponse.ok) {
|
|
localStorage.removeItem('linklogAccessToken');
|
|
showSignedOutState();
|
|
return;
|
|
}
|
|
|
|
const user = await sessionResponse.json();
|
|
if (!user.is_admin) {
|
|
showUnauthorizedState();
|
|
return;
|
|
}
|
|
|
|
await Promise.all([loadUsers(), loadPlugins()]);
|
|
showAdminState(true);
|
|
}
|
|
|
|
async function loadPlugins() {
|
|
const response = await fetch('/api/admin/plugins', {headers: authHeaders()});
|
|
if (!response.ok) throw new Error('Could not load plugins');
|
|
renderPlugins(await response.json());
|
|
}
|
|
|
|
async function updatePlugin(plugin, button) {
|
|
button.disabled = true;
|
|
const response = await fetch(`/api/admin/plugins/${encodeURIComponent(plugin.name)}`, {
|
|
method: 'PUT',
|
|
headers: authHeaders(true),
|
|
body: JSON.stringify({enabled: !plugin.enabled}),
|
|
});
|
|
if (response.ok) {
|
|
await loadPlugins();
|
|
} else {
|
|
button.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function removeUser(user, button) {
|
|
if (!window.confirm(`Remove ${user.username}?`)) return;
|
|
button.disabled = true;
|
|
const response = await fetch(`/api/admin/users/${encodeURIComponent(user.id)}`, {
|
|
method: 'DELETE',
|
|
headers: authHeaders(),
|
|
});
|
|
if (response.ok) {
|
|
await loadUsers();
|
|
} else {
|
|
button.disabled = false;
|
|
}
|
|
}
|
|
|
|
async function updateUserPrivilege(user, checkbox) {
|
|
checkbox.disabled = true;
|
|
const response = await fetch(`/api/admin/users/${encodeURIComponent(user.id)}`, {
|
|
method: 'PUT',
|
|
headers: authHeaders(true),
|
|
body: JSON.stringify({is_admin: checkbox.checked}),
|
|
});
|
|
if (response.ok) {
|
|
await loadUsers();
|
|
} else {
|
|
checkbox.checked = user.is_admin;
|
|
checkbox.disabled = false;
|
|
}
|
|
}
|
|
|
|
userForm.addEventListener('submit', async (event) => {
|
|
event.preventDefault();
|
|
const values = Object.fromEntries(new FormData(userForm));
|
|
values.is_admin = userForm.elements.is_admin.checked;
|
|
const response = await fetch('/api/admin/users', {
|
|
method: 'POST',
|
|
headers: authHeaders(true),
|
|
body: JSON.stringify(values),
|
|
});
|
|
const status = document.querySelector('#user-status');
|
|
status.textContent = response.ok ? 'User added.' : 'Could not add user.';
|
|
status.style.color = response.ok ? '#94e2d5' : '#f38ba8';
|
|
if (response.ok) {
|
|
userForm.reset();
|
|
await loadUsers();
|
|
}
|
|
});
|
|
|
|
loadAdminState().catch((error) => {
|
|
showAdminState(false);
|
|
adminAuthNotice.textContent = accessToken
|
|
? `Could not verify administrator access: ${error.message}`
|
|
: 'Administrator sign-in required.';
|
|
adminAuthNotice.classList.remove('hidden');
|
|
userList.textContent = '';
|
|
pluginList.textContent = '';
|
|
});
|
|
})();
|