106 lines
4.0 KiB
JavaScript
106 lines
4.0 KiB
JavaScript
async function api(method, path, body) {
|
|
const opts = {
|
|
method,
|
|
headers: { "Content-Type": "application/json" },
|
|
credentials: "include",
|
|
};
|
|
if (body !== undefined) opts.body = JSON.stringify(body);
|
|
const res = await fetch(apiURL(path), opts);
|
|
if (res.status === 204) return null;
|
|
let data = null;
|
|
const text = await res.text();
|
|
if (text) {
|
|
try { data = JSON.parse(text); } catch (e) { data = text; }
|
|
}
|
|
if (!res.ok) {
|
|
const err = new Error((data && data.error) || res.statusText);
|
|
err.status = res.status;
|
|
err.data = data;
|
|
throw err;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
const API = {
|
|
login: (u, p) => api("POST", "/api/login", { username: String(u || "").toLowerCase().trim(), password: p }),
|
|
logout: () => api("POST", "/api/logout"),
|
|
me: () => api("GET", "/api/me"),
|
|
updateMe: (b) => api("PATCH", "/api/me", b),
|
|
|
|
listUsers: () => api("GET", "/api/users"),
|
|
createUser: (b) => api("POST", "/api/users", b),
|
|
updateUser: (id, b) => api("PATCH", `/api/users/${id}`, b),
|
|
deleteUser: (id) => api("DELETE", `/api/users/${id}`),
|
|
|
|
listCompetitions: () => api("GET", "/api/competitions"),
|
|
createCompetition: (b) => api("POST", "/api/competitions", b),
|
|
getCompetition: (id) => api("GET", `/api/competitions/${id}`),
|
|
updateCompetition: (id, b) => api("PATCH", `/api/competitions/${id}`, b),
|
|
deleteCompetition: (id) => api("DELETE", `/api/competitions/${id}`),
|
|
closeCompetition: (id) => api("POST", `/api/competitions/${id}/close`),
|
|
reopenCompetition: (id) => api("POST", `/api/competitions/${id}/reopen`),
|
|
|
|
listMembers: (id) => api("GET", `/api/competitions/${id}/members`),
|
|
addMember: (id, b) => api("POST", `/api/competitions/${id}/members`, b),
|
|
removeMember: (id, uid) => api("DELETE", `/api/competitions/${id}/members/${uid}`),
|
|
|
|
listPilots: (id) => api("GET", `/api/competitions/${id}/pilots`),
|
|
createPilot: (id, b) => api("POST", `/api/competitions/${id}/pilots`, b),
|
|
updatePilot: (id, pid, b) => api("PATCH", `/api/competitions/${id}/pilots/${pid}`, b),
|
|
deletePilot: (id, pid) => api("DELETE", `/api/competitions/${id}/pilots/${pid}`),
|
|
importPilots: async (id, csv) => {
|
|
const res = await fetch(apiURL(`/api/competitions/${id}/pilots/import`), {
|
|
method: "POST",
|
|
headers: { "Content-Type": "text/csv" },
|
|
body: csv,
|
|
credentials: "include",
|
|
});
|
|
if (!res.ok) throw new Error("import_failed");
|
|
return res.json();
|
|
},
|
|
|
|
listPenalties: (id) => api("GET", `/api/competitions/${id}/penalties`),
|
|
createPenalty: (id, b) => api("POST", `/api/competitions/${id}/penalties`, b),
|
|
updatePenalty: (id, pid, b) => api("PATCH", `/api/competitions/${id}/penalties/${pid}`, b),
|
|
deletePenalty: (id, pid) => api("DELETE", `/api/competitions/${id}/penalties/${pid}`),
|
|
applyPenalties: (id, b) => api("POST", `/api/competitions/${id}/penalties/apply`, b),
|
|
exportPenaltiesCSV: async (id) => {
|
|
const res = await fetch(apiURL(`/api/competitions/${id}/penalties.csv`), { credentials: "include" });
|
|
if (!res.ok) throw new Error("export_failed");
|
|
return res.blob();
|
|
},
|
|
|
|
listRules: (lang) => api("GET", `/api/rules${lang ? "?lang=" + encodeURIComponent(lang) : ""}`),
|
|
listRuleLanguages: () => api("GET", "/api/rules/languages"),
|
|
};
|
|
|
|
function openCompetitionWS(id, handlers) {
|
|
const url = wsURL(`/api/competitions/${id}/ws`);
|
|
let ws = null;
|
|
let closed = false;
|
|
let backoff = 1000;
|
|
|
|
function connect() {
|
|
ws = new WebSocket(url);
|
|
ws.onopen = () => { backoff = 1000; if (handlers.onopen) handlers.onopen(); };
|
|
ws.onmessage = (e) => {
|
|
try {
|
|
const msg = JSON.parse(e.data);
|
|
if (handlers.onmessage) handlers.onmessage(msg);
|
|
} catch (err) {}
|
|
};
|
|
ws.onclose = () => {
|
|
if (handlers.onclose) handlers.onclose();
|
|
if (!closed) {
|
|
setTimeout(connect, backoff);
|
|
backoff = Math.min(backoff * 2, 15000);
|
|
}
|
|
};
|
|
ws.onerror = () => { try { ws.close(); } catch (e) {} };
|
|
}
|
|
connect();
|
|
return {
|
|
close: () => { closed = true; try { ws && ws.close(); } catch (e) {} }
|
|
};
|
|
}
|