ano-mr-site/front/data-manager.js

189 lines
7.3 KiB
JavaScript
Raw Permalink 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.

/**
* @typedef {Object} MarkerData
* @property {Blob} background - фон
* @property {Array<Blob>} images - изображения
* @property {Array<Blob>} slider - слайды
*/
/** Путь к php-прослойке, работающая с бд и внутренним хранилищем */
const db_get_filepath = '../db_get.php'
/** Дефолтный срок годности кэша : 2 часа */
const CACHE_DEFAULT_TTL_MS = 2 * 60 * 60 * 1000;
/**
* Получить объект кэша, если поддерживается Cache API
* @returns {Promise<Cache|null>}
*/
async function getCache() {
if (typeof caches !== 'undefined') {
return await caches.open('cache');
}
return null;
}
/**
* Излечь данные из ответа
* @param {string} type - тип извлекаемых данных
* @param {Response} response - ответ
* @returns {Promise<Object|Blob>}
*/
const extract_file = recovery(async (type, response) => {
switch(type) {
case 'file':
return await response.blob();
case 'json':
return await response.json();
default:
throw new Error('Не определен type');
}
}, (...e) => {throw Error(`Ошибки при извлечении (extract_file): ${e.join(', ')}`)} );
/**
* @param {string} path - путь (либо локальный или https)
* @param {object} options - параметры fetch
* @param {int} timeout - секундомер
*/
const fetch_with_timeout = recovery(async (path, options = {}, timeout = 10 * 1000) => {
const controller = new AbortController();
const signal = controller.signal;
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(path, {...options, signal});
try { return response; }
finally { clearTimeout(timeoutId); }
}, (e) => {
if(e.name === 'AbortError')
console.log('Запрос прерван по таймауту:', e);
else console.log(e);
}, (...e) => {throw Error(`Ошибки при запросе с таймером (fetch_with_timeout): ${e.join(', ')}`)} );
/**
* Вернуть данные из ответа, кэшируя перед этим (если Cache API есть)
* @param {string} type - тип извлекаемых данных
* @param {string} path - путь (либо локальный или https)
* @param {string} cache_key - ключ кэша
* @param {Object} body_req - тело запроса
* @returns {Promise<Object|Blob>}
*/
const return_fetch = recovery(async (type, path, cache_key, body_req) => {
const cache = await getCache();
const response = !body_req
? await fetch_with_timeout(path)
: await fetch_with_timeout(path, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body_req)
});
if (cache) {
const cloned = response.clone();
const blob = await cloned.blob();
const headers = new Headers(cloned.headers);
headers.set('date', new Date().toUTCString());
await cache.put(
cache_key,
new Response(blob, {
status: cloned.status,
statusText: cloned.statusText,
headers
})
);
}
return await extract_file(type, response);
}, (...e) => {throw Error(`Ошибки при запросе и кэшировании (return_fetch): ${e.join(', ')}`)} );
/**
* Получить данные из пути (локальный или https), с поддержкой кэша, если он есть
* @param {string} type - тип извлекаемых данных
* @param {string} path - путь (либо локальный или https)
* @param {Object|null} body_req - тело запроса
* @returns {Promise<Object|Blob>}
*/
const get = recovery(async (type, path, body_req = null) => {
const cache = await getCache();
const cache_key = `${path}?type=${type}&file=${encodeURIComponent(JSON.stringify(body_req || {}))}`;
if (cache) {
const cached_response = await cache.match(cache_key);
if (cached_response) {
const expires_value = cached_response.headers.get('expires');
const date_value = cached_response.headers.get('date');
const exp = expires_value && expires_value !== '-1'
? Date.parse(expires_value)
: Date.parse(date_value ?? '') + CACHE_DEFAULT_TTL_MS;
if (Date.now() < exp) {
return await extract_file(type, cached_response);
}
await cache.delete(cache_key);
}
}
return await return_fetch(type, path, cache_key, body_req);
}, (...e) => {throw Error(`Ошибки при получении (get): ${e.join(', ')}`)} );
/**
* Получить данные на диске (абсолютный/относительный путь)
* @param {string} filepath - путь к файлу
* @returns {Promise<Blob>} - без json
*/
const get_local = recovery(async (filepath) => {
return await get('file', db_get_filepath, {
mode: 'file',
path: filepath
});
}, (...e) => {throw Error(`Ошибки при локальном получении (get_local): ${e.join(', ')}`)} );
/**
* Получить данные без кэширования
* @param {string} type - тип извлекаемых данных
* @param {string} path - путь (либо локальный или https)
* @returns {Promise<Object|Blob>}
*/
const get_no_cached = recovery(async (type, path) => {
const file = await fetch_with_timeout(path);
return await extract_file(type, file);
}, (...e) => {throw Error(`Ошибки при получении без кэширования (get_no_cached): ${e.join(', ')}`)} );
/**
* Получить материалы поселения для инфо. страницы
* @param {MarkerData} properties
* @returns {Promise<{ background: Blob, images: Blob[], slider: Blob[]}>}
*/
const get_for_info_page = recovery(async (properties) => {
const background = await get_local(properties.background);
const images = await Promise.all(
(properties.images || []).map(i_path => get_local(i_path))
);
const slider = Array.isArray(properties.slider)
? await Promise.all(properties.slider.map(s_path => get_local(s_path)))
: [];
return {
background,
images,
slider,
};
}, (...e) => {throw Error(`Ошибки при получении материалов для страницы (get_for_info_page): ${e.join(', ')}`)} );
/**
* Получить иконки для стилизации страниц.
* @returns {Promise<Object|Blob>}
*/
const get_icons = recovery(async () => await get_no_cached('json', './data/icons.json')
, (...e) => {throw Error(`Ошибки при получении данных для стилизации (get_icons): ${e.join(', ')}`)} );
/**
* Получить все geojson'ы из базы данных
* @returns {Promise<Object>}
*/
const get_all_geojsons = recovery(async () => await get('json', db_get_filepath, {'mode': 'geojsons'})
, (...e) => {throw Error(`Ошибки при получении geojson'ов из бд (get_all_geojsons): ${e.join(', ')}`)} );