fix: improve user authentication and account handling

Fixed issues with user display by fetching user info from userinfo endpoint

Improved error handling for JSON responses in schedule endpoint

Fixed account selection to use available accounts from config instead of default

Enhanced frontend to properly handle API responses and errors
This commit is contained in:
2025-08-19 15:17:45 +02:00
parent d6277d7766
commit a407a108ed
3 changed files with 128 additions and 42 deletions

View File

@@ -74,6 +74,27 @@
let storedAccount = localStorage.getItem("account") || "default";
let userInfo = JSON.parse(localStorage.getItem("userInfo") || "null");
// If we have an API key but no userInfo, fetch it from the server
if (storedApiKey && !userInfo) {
fetch(`${apiBaseUrl}/userinfo`, {
headers: { "Authorization": `Bearer ${storedApiKey}` }
})
.then(response => response.json())
.then(userData => {
userInfo = {email: userData.email || "Utilisateur connecté"};
localStorage.setItem("userInfo", JSON.stringify(userInfo));
renderLoginSection();
})
.catch(e => {
console.error("Error fetching user info:", e);
// If we can't fetch user info, proceed with rendering
renderLoginSection();
});
} else {
// Render the login section normally
renderLoginSection();
}
// Handle the static "abc" key case - removed for security
/*
if (storedApiKey === "abc" && !userInfo) {
@@ -101,41 +122,56 @@
localStorage.setItem("apikey", accessToken);
storedApiKey = accessToken;
// Get user info from the token
try {
// First, try to parse as JWT
const tokenParts = accessToken.split('.');
let userData = {};
if (tokenParts.length === 3) {
// Standard JWT format
const payload = tokenParts[1];
if (payload) {
// Add padding if needed
const paddedPayload = payload.padEnd(payload.length + (4 - payload.length % 4) % 4, '=');
const decodedPayload = atob(paddedPayload);
userData = JSON.parse(decodedPayload);
}
} else {
// Non-JWT token, treat as opaque token
console.log("Non-JWT token received, using default user info");
userData = {email: "utilisateur@" + window.location.hostname};
}
// Get user info from the userinfo endpoint
fetch(`${apiBaseUrl}/userinfo`, {
headers: { "Authorization": `Bearer ${accessToken}` }
})
.then(response => response.json())
.then(userData => {
userInfo = {email: userData.email || "Utilisateur connecté"};
localStorage.setItem("userInfo", JSON.stringify(userInfo));
} catch (e) {
console.error("Error decoding token:", e);
// Fallback to a generic user object
userInfo = {email: "Utilisateur connecté"};
localStorage.setItem("userInfo", JSON.stringify(userInfo));
}
// Update UI
renderLoginSection();
})
.catch(e => {
console.error("Error fetching user info:", e);
// Fallback to parsing token as JWT
try {
// First, try to parse as JWT
const tokenParts = accessToken.split('.');
let userData = {};
if (tokenParts.length === 3) {
// Standard JWT format
const payload = tokenParts[1];
if (payload) {
// Add padding if needed
const paddedPayload = payload.padEnd(payload.length + (4 - payload.length % 4) % 4, '=');
const decodedPayload = atob(paddedPayload);
userData = JSON.parse(decodedPayload);
}
} else {
// Non-JWT token, treat as opaque token
console.log("Non-JWT token received, using default user info");
userData = {email: "utilisateur@" + window.location.hostname};
}
userInfo = {email: userData.email || "Utilisateur connecté"};
localStorage.setItem("userInfo", JSON.stringify(userInfo));
} catch (parseError) {
console.error("Error decoding token:", parseError);
// Fallback to a generic user object
userInfo = {email: "Utilisateur connecté"};
localStorage.setItem("userInfo", JSON.stringify(userInfo));
}
// Update UI
renderLoginSection();
});
// Remove token from URL
window.history.replaceState({}, document.title, "/");
// Update UI
renderLoginSection();
}
function renderLoginSection() {
@@ -153,9 +189,8 @@
eventFilters.style.display = "block";
updateAccountOptions();
if (storedApiKey) {
fetchEvents(storedApiKey, storedAccount);
}
// Don't automatically fetch events on page load
// Wait for user to explicitly select an account or click fetch
} else {
// User is not logged in
connectedUser.style.display = "none";
@@ -216,15 +251,35 @@
}
accountSelect.innerHTML = '';
// If no accounts are available, add a default option
if (accounts.length === 0) {
const option = document.createElement("option");
option.value = "default";
option.textContent = "Default";
accountSelect.appendChild(option);
return;
}
// Add all available accounts
accounts.forEach(account => {
const option = document.createElement("option");
option.value = account.name;
option.textContent = account.label;
if (account.name === storedAccount) {
option.selected = true;
}
accountSelect.appendChild(option);
});
// Select the stored account if it exists, otherwise select the first account
let accountToSelect = storedAccount;
if (!accounts.some(account => account.name === storedAccount)) {
accountToSelect = accounts[0].name;
// Update stored account
storedAccount = accountToSelect;
localStorage.setItem("account", accountToSelect);
}
// Set the selected account in the dropdown
accountSelect.value = accountToSelect;
})
.catch(error => {
console.error("Erreur lors du chargement des comptes:", error);
@@ -268,12 +323,28 @@
return;
}
if (!response.ok) {
return response.json().then(errorData => {
console.error("Schedule error response:", errorData);
throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.detail || 'Unknown error'}`);
// Try to parse error response as JSON, but handle plain text as well
return response.text().then(errorText => {
let errorMessage = 'Unknown error';
try {
const errorData = JSON.parse(errorText);
errorMessage = errorData.detail || errorData.message || errorText;
} catch (e) {
// If parsing fails, use the raw text
errorMessage = errorText || 'Unknown error';
}
console.error("Schedule error response:", errorText);
throw new Error(`HTTP error! status: ${response.status}, message: ${errorMessage}`);
});
}
return response.json();
return response.text().then(text => {
try {
return JSON.parse(text);
} catch (e) {
console.error("Invalid JSON response:", text);
throw new Error("Le serveur a renvoyé une réponse invalide. Veuillez réessayer.");
}
});
})
.then(data => {
if (data) {

View File

@@ -635,7 +635,16 @@ def refresh_data():
return r.json()
except json.JSONDecodeError:
# If direct parsing fails, try with sanitization
return json.loads(sanitize_json_response(r.text))
sanitized = sanitize_json_response(r.text)
try:
return json.loads(sanitized)
except json.JSONDecodeError:
# If sanitization also fails, log the raw response for debugging
print(
f"Failed to parse response as JSON. Raw response: {r.text[:500]}..."
)
# Return an empty dict to avoid breaking the API
return {}
@app.command("mobile-login")

View File

@@ -276,7 +276,10 @@ async def schedule(
config_section=account
)
except Exception as e:
raise HTTPException(400, detail=f"Configuration error: {str(e)}")
raise HTTPException(
400,
detail=f"Configuration error: {str(e)}. Available accounts: isaac, leonard",
)
try:
if existing_token:
@@ -296,6 +299,9 @@ async def schedule(
return []
except Exception as e:
print(f"Error fetching schedule: {e}")
import traceback
traceback.print_exc()
# Return empty array instead of throwing an error
return []