Add basic web app and API integration
This commit is contained in:
+96
@@ -0,0 +1,96 @@
|
||||
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: u, 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),
|
||||
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}`),
|
||||
|
||||
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}`),
|
||||
exportPenaltiesURL: (id) => apiURL(`/api/competitions/${id}/penalties.csv`),
|
||||
|
||||
listRules: (lang) => api("GET", `/api/rules${lang ? "?lang=" + encodeURIComponent(lang) : ""}`),
|
||||
};
|
||||
|
||||
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) {} }
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user