Files
duty-teller/webapp/js/api.js
Nikolay Tatarinov c9cf86a8f6 refactor: restructure web application with modular JavaScript and remove legacy code
- Deleted the `app.js` file and migrated its functionality to a modular structure with multiple JavaScript files for better organization and maintainability.
- Updated `index.html` to reference the new `main.js` module, ensuring proper loading of the application.
- Introduced new utility modules for API requests, authentication, calendar handling, and DOM manipulation to enhance code clarity and separation of concerns.
- Enhanced CSS styles for improved layout and theming consistency across the application.
- Added comprehensive comments and documentation to new modules to facilitate future development and understanding.
2026-02-19 15:24:52 +03:00

94 lines
2.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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<object[]>}
*/
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<object[]>}
*/
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);
}
}