/** * Network requests: duties and calendar events. */ import { FETCH_TIMEOUT_MS } from "./constants.js"; import { getInitData } from "./auth.js"; /** * Build fetch options with init data header and timeout abort. * @param {string} initData - Telegram init data * @returns {{ headers: object, signal: AbortSignal, timeoutId: number }} */ export function buildFetchOptions(initData) { const headers = {}; if (initData) headers["X-Telegram-Init-Data"] = initData; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); return { headers, signal: controller.signal, timeoutId }; } /** * Fetch duties for date range. Throws ACCESS_DENIED error on 403. * @param {string} from - YYYY-MM-DD * @param {string} to - YYYY-MM-DD * @returns {Promise} */ export async function fetchDuties(from, to) { const base = window.location.origin; const url = base + "/api/duties?from=" + encodeURIComponent(from) + "&to=" + encodeURIComponent(to); const initData = getInitData(); const opts = buildFetchOptions(initData); try { const res = await fetch(url, { headers: opts.headers, signal: opts.signal }); if (res.status === 403) { let detail = "Доступ запрещён"; try { const body = await res.json(); if (body && body.detail !== undefined) { detail = typeof body.detail === "string" ? body.detail : (body.detail.msg || JSON.stringify(body.detail)); } } catch (parseErr) { /* ignore */ } const err = new Error("ACCESS_DENIED"); err.serverDetail = detail; throw err; } if (!res.ok) throw new Error("Ошибка загрузки"); return res.json(); } catch (e) { if (e.name === "AbortError") { throw new Error("Не удалось загрузить данные. Проверьте интернет."); } throw e; } finally { clearTimeout(opts.timeoutId); } } /** * Fetch calendar events for range. Returns [] on non-200 or error. Does not throw for 403. * @param {string} from - YYYY-MM-DD * @param {string} to - YYYY-MM-DD * @returns {Promise} */ export async function fetchCalendarEvents(from, to) { const base = window.location.origin; const url = base + "/api/calendar-events?from=" + encodeURIComponent(from) + "&to=" + encodeURIComponent(to); const initData = getInitData(); const opts = buildFetchOptions(initData); try { const res = await fetch(url, { headers: opts.headers, signal: opts.signal }); if (!res.ok) return []; return res.json(); } catch (e) { return []; } finally { clearTimeout(opts.timeoutId); } }