'use strict'; // Tiny fetch wrapper. Tracks the CSRF token (from GET /api/me or login) and // attaches it to every state-changing request as X-CSRF-Token. window.API = (function () { let csrfToken = null; async function request(method, url, body) { const headers = {}; if (body !== undefined) headers['Content-Type'] = 'application/json'; if (method !== 'GET' && csrfToken) headers['X-CSRF-Token'] = csrfToken; const res = await fetch(url, { method, headers, credentials: 'same-origin', body: body !== undefined ? JSON.stringify(body) : undefined, }); let data = null; const text = await res.text(); if (text) { try { data = JSON.parse(text); } catch (_) { data = { raw: text }; } } if (!res.ok) { // An expired/invalid session (401) on any call other than the login POST // means we should send the user back to sign in, rather than leaving the // page in a broken half-loaded state with only a console error. const onLoginPage = window.location.pathname.endsWith('/login.html'); const isLoginCall = url.endsWith('/api/login'); if (res.status === 401 && !onLoginPage && !isLoginCall) { window.location.href = '/login.html'; } const err = new Error((data && data.error) || `Request failed (${res.status})`); err.status = res.status; throw err; } if (data && data.csrfToken) csrfToken = data.csrfToken; return data; } return { setCsrf: (t) => { csrfToken = t; }, get: (url) => request('GET', url), post: (url, body) => request('POST', url, body || {}), patch: (url, body) => request('PATCH', url, body || {}), put: (url, body) => request('PUT', url, body || {}), del: (url) => request('DELETE', url), }; })();