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

189 lines
8.2 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.

/**
* @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;
/**
* Излечь данные из ответа
* @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) => {
/** Штука для преждевременного завершения fetch */
const controller = new AbortController()
/** Как я понял, якорь для fetch */
const signal = controller.signal
/** Сработает завершение после истечения timeout */
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(', ')}`)} )
/**
* Вернуть данные из ответа, кэшируя перед этим
* @param {string} type - тип извлекаемых данных
* @param {string} path - путь (либо локальный или https)
* @param {Object} body_req - тело запроса
* @returns {Promise<Object|Blob>}
*/
const return_fetch = recovery(async (type, path, cache_key, body_req) => {
const cache = await caches.open('cache')
/** Запрос по пути (если есть тело, то локальное обращение к файлу) */
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)
})
/** Клонирование, т.к blob/json необратимый процесс. */
const cloned = response.clone()
const blob = await cloned.blob()
const headers = new Headers(cloned.headers)
/** Добавление даты создания в заголовок 'date' кешируемого ответа. */
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 {*} type - тип извлекаемых данных
* @param {*} path - путь (либо локальный или https)
* @param {*} body_req - тело запроса
* @returns {Promise<Object|Blob>}
*/
const get = recovery(async (type, path, body_req = null) => {
const cache = await caches.open('cache')
/** Ключ для добавления или получения кэш-данных. */
const cache_key = `${path}?type=${type}&file=${encodeURIComponent(JSON.stringify(body_req || {}))}`
/** Результат поиска ответа в кэше */
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
/** Если exp больше, вернуть ответ из кэша, иначе удалить протухшие данные */
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
* - нужные свойства (пути) из свойств feature.
* @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(', ')}`)} )