revoked access to website for coaches, added web-specific tokens

This commit is contained in:
2026-05-26 15:49:19 +02:00
parent 7671c9f329
commit 2181d1731e
36 changed files with 129 additions and 41 deletions

View File

@ -7,6 +7,7 @@ function getToken() {
async function apiFetch(endpoint, options = {}) {
const token = getToken();
const headers = options.headers || {};
headers['X-QDB-Client'] = 'web';
if (token) headers['Authorization'] = `Bearer ${token}`;
if (!(options.body instanceof FormData)) {
headers['Content-Type'] = 'application/json; charset=UTF-8';

View File

@ -19,9 +19,26 @@ export function getRole() {
export function getUser() {
return localStorage.getItem('qdb_user') || '';
}
const WEBSITE_ROLES = ['admin', 'supervisor'];
export function canAccessWebsite() {
return WEBSITE_ROLES.includes(getRole());
}
export function canEdit() {
const r = getRole();
return r === 'admin' || r === 'supervisor';
return canAccessWebsite();
}
function clearSession() {
localStorage.removeItem('qdb_token');
localStorage.removeItem('qdb_user');
localStorage.removeItem('qdb_role');
}
function rejectCoachSession() {
clearSession();
showToast('Coach accounts must use the mobile app.', 'error');
navigate('#/login');
}
export function showToast(message, type = 'info') {
@ -40,6 +57,7 @@ export function showToast(message, type = 'info') {
function authGuard(handler) {
return (params) => {
if (!isLoggedIn()) { navigate('#/login'); return; }
if (!canAccessWebsite()) { rejectCoachSession(); return; }
return handler(params);
};
}
@ -95,12 +113,14 @@ addRoute('/translations', roleGuard(['admin', 'supervisor'], translationsPage));
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('logoutBtn').addEventListener('click', (e) => {
e.preventDefault();
localStorage.removeItem('qdb_token');
localStorage.removeItem('qdb_user');
localStorage.removeItem('qdb_role');
clearSession();
navigate('#/login');
});
if (isLoggedIn() && !canAccessWebsite()) {
clearSession();
}
window.addEventListener('hashchange', updateNav);
updateNav();
startRouter();

View File

@ -55,7 +55,10 @@ export function loginPage() {
try {
const res = await fetch('../api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
headers: {
'Content-Type': 'application/json',
'X-QDB-Client': 'web',
},
body: JSON.stringify({ username, password }),
});
const json = await res.json();
@ -65,6 +68,11 @@ export function loginPage() {
return;
}
const d = json.data;
if (d.role === 'coach') {
errEl.textContent = 'Coach accounts can only sign in through the mobile app.';
errEl.style.display = '';
return;
}
if (d.mustChangePassword) {
pendingUsername = username;
pendingTempToken = d.token;
@ -95,7 +103,10 @@ export function loginPage() {
}
const oldPw = document.getElementById('password').value;
try {
const headers = { 'Content-Type': 'application/json' };
const headers = {
'Content-Type': 'application/json',
'X-QDB-Client': 'web',
};
if (pendingTempToken) {
headers['Authorization'] = `Bearer ${pendingTempToken}`;
}
@ -111,6 +122,11 @@ export function loginPage() {
return;
}
const d = json.data;
if (d.role === 'coach') {
errEl.textContent = 'Coach accounts can only sign in through the mobile app.';
errEl.style.display = '';
return;
}
localStorage.setItem('qdb_token', d.token);
localStorage.setItem('qdb_user', d.user);
localStorage.setItem('qdb_role', d.role);