feat: add account selection support in web UI

Add support for multiple MyIce accounts in the web interface:

- Create /accounts endpoint to fetch available accounts from config files

- Update index.html to dynamically show available accounts

- Modify /schedule and /game endpoints to accept account parameter

- Store selected account in localStorage for persistence
This commit is contained in:
2025-08-18 22:10:24 +02:00
parent 6eb9598012
commit 66b93af6b1
2 changed files with 110 additions and 11 deletions

View File

@@ -16,6 +16,12 @@
<div id="apikeyContainer" class="mb-3"></div> <div id="apikeyContainer" class="mb-3"></div>
<div id="eventFilters" style="display: none;"> <div id="eventFilters" style="display: none;">
<div class="mb-3">
<label for="account" class="form-label">Sélectionner un compte</label>
<select id="account" class="form-select">
<option value="default">Compte par défaut</option>
</select>
</div>
<div class="mb-3"> <div class="mb-3">
<label for="agegroup" class="form-label">Filtrer par groupe d'âge</label> <label for="agegroup" class="form-label">Filtrer par groupe d'âge</label>
<select id="agegroup" class="form-select"> <select id="agegroup" class="form-select">
@@ -46,6 +52,7 @@
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
const apikeyContainer = document.getElementById("apikeyContainer"); const apikeyContainer = document.getElementById("apikeyContainer");
const eventFilters = document.getElementById("eventFilters"); const eventFilters = document.getElementById("eventFilters");
const accountSelect = document.getElementById("account");
const agegroupSelect = document.getElementById("agegroup"); const agegroupSelect = document.getElementById("agegroup");
const eventList = document.getElementById("eventList"); const eventList = document.getElementById("eventList");
const fetchButton = document.getElementById("fetchEvents"); const fetchButton = document.getElementById("fetchEvents");
@@ -54,16 +61,20 @@
let storedApiKey = localStorage.getItem("apikey"); let storedApiKey = localStorage.getItem("apikey");
let lastFetchedEvents = []; let lastFetchedEvents = [];
let storedAccount = localStorage.getItem("account") || "default";
function renderApiKeyInput() { function renderApiKeyInput() {
if (storedApiKey) { if (storedApiKey) {
apikeyContainer.innerHTML = `<button id="disconnect" class="btn btn-danger">Déconnecter</button>`; apikeyContainer.innerHTML = `<button id="disconnect" class="btn btn-danger">Déconnecter</button>`;
document.getElementById("disconnect").addEventListener("click", () => { document.getElementById("disconnect").addEventListener("click", () => {
localStorage.removeItem("apikey"); localStorage.removeItem("apikey");
localStorage.removeItem("account");
location.reload(); location.reload();
}); });
eventFilters.style.display = "block"; eventFilters.style.display = "block";
fetchEvents(storedApiKey); // Load available accounts from server or use predefined ones
updateAccountOptions();
fetchEvents(storedApiKey, storedAccount);
} else { } else {
apikeyContainer.innerHTML = ` apikeyContainer.innerHTML = `
<label for="apikey" class="form-label">API Key</label> <label for="apikey" class="form-label">API Key</label>
@@ -90,22 +101,58 @@
} }
} }
function updateAccountOptions() {
// Fetch available accounts from the server
fetch(`${apiBaseUrl}/accounts`, {
headers: { "Authorization": `Bearer ${storedApiKey}` }
})
.then(response => response.json())
.then(accounts => {
accountSelect.innerHTML = '';
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);
});
})
.catch(error => {
console.error("Erreur lors du chargement des comptes:", error);
// Fallback to default options
accountSelect.innerHTML = `
<option value="default" ${storedAccount === "default" ? "selected" : ""}>Compte par défaut</option>
`;
});
}
renderApiKeyInput(); renderApiKeyInput();
accountSelect.addEventListener("change", () => {
const selectedAccount = accountSelect.value;
localStorage.setItem("account", selectedAccount);
if (storedApiKey) {
fetchEvents(storedApiKey, selectedAccount);
}
});
fetchButton.addEventListener("click", () => { fetchButton.addEventListener("click", () => {
if (!storedApiKey) { if (!storedApiKey) {
alert("Veuillez entrer une clé API"); alert("Veuillez entrer une clé API");
return; return;
} }
fetchEvents(storedApiKey); const selectedAccount = accountSelect.value;
fetchEvents(storedApiKey, selectedAccount);
}); });
agegroupSelect.addEventListener("change", () => { agegroupSelect.addEventListener("change", () => {
displayEvents(lastFetchedEvents); displayEvents(lastFetchedEvents);
}); });
function fetchEvents(apiKey) { function fetchEvents(apiKey, account) {
fetch(`${apiBaseUrl}/schedule`, { fetch(`${apiBaseUrl}/schedule?account=${account}`, {
headers: { "Authorization": `Bearer ${apiKey}` } headers: { "Authorization": `Bearer ${apiKey}` }
}) })
.then(response => response.json()) .then(response => response.json())
@@ -156,7 +203,8 @@
} }
function fetchEventDetails(eventId) { function fetchEventDetails(eventId) {
fetch(`${apiBaseUrl}/game/${eventId}`, { const selectedAccount = accountSelect.value;
fetch(`${apiBaseUrl}/game/${eventId}?account=${selectedAccount}`, {
headers: { "Authorization": `Bearer ${storedApiKey}` } headers: { "Authorization": `Bearer ${storedApiKey}` }
}) })
.then(response => response.json()) .then(response => response.json())

View File

@@ -74,35 +74,86 @@ async def favico():
@app.get("/schedule") @app.get("/schedule")
async def schedule( async def schedule(
headers: Annotated[AuthHeaders, Header()], headers: Annotated[AuthHeaders, Header()],
account: str = "default",
): ):
if not headers.authorized(): if not headers.authorized():
raise HTTPException(401, detail="get out") raise HTTPException(401, detail="get out")
username, password, userid, existing_token, club_id = myice.get_login() username, password, userid, existing_token, club_id = myice.get_login(
config_section=account
)
if existing_token: if existing_token:
myice.userdata = { myice.userdata = {
"id": userid, "id": userid,
"id_club": 186, "id_club": club_id or 186,
"token": existing_token, "token": existing_token,
} }
else: else:
myice.userdata = myice.mobile_login() myice.userdata = myice.mobile_login(config_section=account)
return myice.refresh_data()["club_games"] return myice.refresh_data()["club_games"]
@app.get("/accounts")
async def accounts(
headers: Annotated[AuthHeaders, Header()],
):
if not headers.authorized():
raise HTTPException(401, detail="get out")
# Import configparser to read the available sections
import configparser
from pathlib import Path
config = configparser.ConfigParser()
config_files = [
Path("~/.config/myice.ini").expanduser(),
Path("myice.ini"),
]
# Read all config files
for config_file in config_files:
if config_file.exists():
try:
config.read(config_file, encoding="utf-8")
except Exception:
# Try without specifying encoding
config.read(config_file)
# Get all sections (accounts) from the config
accounts = []
for section in config.sections():
if section != "DEFAULT": # Skip DEFAULT section
# Capitalize first letter for display
label = (
section[0].upper() + section[1:]
if len(section) > 1
else section.upper()
)
accounts.append({"name": section, "label": label})
# If no accounts found, return default
if not accounts:
accounts = [{"name": "default", "label": "Default"}]
return accounts
@app.get("/game/{game_id}") @app.get("/game/{game_id}")
async def game( async def game(
headers: Annotated[AuthHeaders, Header()], headers: Annotated[AuthHeaders, Header()],
game_id: int, game_id: int,
account: str = "default",
): ):
username, password, userid, existing_token, club_id = myice.get_login() username, password, userid, existing_token, club_id = myice.get_login(
config_section=account
)
if existing_token: if existing_token:
myice.userdata = { myice.userdata = {
"id": userid, "id": userid,
"id_club": 186, "id_club": club_id or 186,
"token": existing_token, "token": existing_token,
} }
else: else:
myice.userdata = myice.mobile_login() myice.userdata = myice.mobile_login(config_section=account)
# data = refresh_data() # data = refresh_data()
with requests.post( with requests.post(