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:
151
index.html
151
index.html
@@ -74,6 +74,27 @@
|
|||||||
let storedAccount = localStorage.getItem("account") || "default";
|
let storedAccount = localStorage.getItem("account") || "default";
|
||||||
let userInfo = JSON.parse(localStorage.getItem("userInfo") || "null");
|
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
|
// Handle the static "abc" key case - removed for security
|
||||||
/*
|
/*
|
||||||
if (storedApiKey === "abc" && !userInfo) {
|
if (storedApiKey === "abc" && !userInfo) {
|
||||||
@@ -101,41 +122,56 @@
|
|||||||
localStorage.setItem("apikey", accessToken);
|
localStorage.setItem("apikey", accessToken);
|
||||||
storedApiKey = accessToken;
|
storedApiKey = accessToken;
|
||||||
|
|
||||||
// Get user info from the token
|
// Get user info from the userinfo endpoint
|
||||||
try {
|
fetch(`${apiBaseUrl}/userinfo`, {
|
||||||
// First, try to parse as JWT
|
headers: { "Authorization": `Bearer ${accessToken}` }
|
||||||
const tokenParts = accessToken.split('.');
|
})
|
||||||
let userData = {};
|
.then(response => response.json())
|
||||||
|
.then(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é"};
|
userInfo = {email: userData.email || "Utilisateur connecté"};
|
||||||
localStorage.setItem("userInfo", JSON.stringify(userInfo));
|
localStorage.setItem("userInfo", JSON.stringify(userInfo));
|
||||||
} catch (e) {
|
|
||||||
console.error("Error decoding token:", e);
|
// Update UI
|
||||||
// Fallback to a generic user object
|
renderLoginSection();
|
||||||
userInfo = {email: "Utilisateur connecté"};
|
})
|
||||||
localStorage.setItem("userInfo", JSON.stringify(userInfo));
|
.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
|
// Remove token from URL
|
||||||
window.history.replaceState({}, document.title, "/");
|
window.history.replaceState({}, document.title, "/");
|
||||||
|
|
||||||
// Update UI
|
|
||||||
renderLoginSection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderLoginSection() {
|
function renderLoginSection() {
|
||||||
@@ -153,9 +189,8 @@
|
|||||||
|
|
||||||
eventFilters.style.display = "block";
|
eventFilters.style.display = "block";
|
||||||
updateAccountOptions();
|
updateAccountOptions();
|
||||||
if (storedApiKey) {
|
// Don't automatically fetch events on page load
|
||||||
fetchEvents(storedApiKey, storedAccount);
|
// Wait for user to explicitly select an account or click fetch
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// User is not logged in
|
// User is not logged in
|
||||||
connectedUser.style.display = "none";
|
connectedUser.style.display = "none";
|
||||||
@@ -216,15 +251,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
accountSelect.innerHTML = '';
|
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 => {
|
accounts.forEach(account => {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = account.name;
|
option.value = account.name;
|
||||||
option.textContent = account.label;
|
option.textContent = account.label;
|
||||||
if (account.name === storedAccount) {
|
|
||||||
option.selected = true;
|
|
||||||
}
|
|
||||||
accountSelect.appendChild(option);
|
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 => {
|
.catch(error => {
|
||||||
console.error("Erreur lors du chargement des comptes:", error);
|
console.error("Erreur lors du chargement des comptes:", error);
|
||||||
@@ -268,12 +323,28 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return response.json().then(errorData => {
|
// Try to parse error response as JSON, but handle plain text as well
|
||||||
console.error("Schedule error response:", errorData);
|
return response.text().then(errorText => {
|
||||||
throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.detail || 'Unknown error'}`);
|
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 => {
|
.then(data => {
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|||||||
@@ -635,7 +635,16 @@ def refresh_data():
|
|||||||
return r.json()
|
return r.json()
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
# If direct parsing fails, try with sanitization
|
# 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")
|
@app.command("mobile-login")
|
||||||
|
|||||||
@@ -276,7 +276,10 @@ async def schedule(
|
|||||||
config_section=account
|
config_section=account
|
||||||
)
|
)
|
||||||
except Exception as e:
|
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:
|
try:
|
||||||
if existing_token:
|
if existing_token:
|
||||||
@@ -296,6 +299,9 @@ async def schedule(
|
|||||||
return []
|
return []
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error fetching schedule: {e}")
|
print(f"Error fetching schedule: {e}")
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
traceback.print_exc()
|
||||||
# Return empty array instead of throwing an error
|
# Return empty array instead of throwing an error
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user