Files

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) {} }
};
}