178 lines
4.5 KiB
JavaScript
178 lines
4.5 KiB
JavaScript
import { SsrDataFormatError } from "../others/errors";
|
|
import ssrConfig from "../ssr-config";
|
|
import { MINUTE } from "../utils/date";
|
|
import createNetworkCache from "./cache";
|
|
import {
|
|
SsrHttpClientError,
|
|
SsrHttpNotFoundError,
|
|
SsrHttpRateLimitError,
|
|
SsrHttpResponseError,
|
|
SsrHttpServerError,
|
|
SsrHttpUnauthenticatedError,
|
|
SsrHttpUnauthorizedError,
|
|
SsrHttpUnprocessableEntityError,
|
|
SsrNetworkError,
|
|
} from "./errors";
|
|
import { parseRateLimitHeaders } from "./utils";
|
|
|
|
const networkCache = createNetworkCache();
|
|
|
|
const checkResponse = (response) => {
|
|
if (response.ok) {
|
|
// response.status >= 200 && response.status < 300
|
|
return response;
|
|
}
|
|
|
|
switch (true) {
|
|
case response.status === 401:
|
|
throw new SsrHttpUnauthenticatedError(response);
|
|
case response.status === 403:
|
|
throw new SsrHttpUnauthorizedError(response);
|
|
case response.status === 404:
|
|
throw new SsrHttpNotFoundError(response);
|
|
case response.status === 422:
|
|
throw new SsrHttpUnprocessableEntityError(response);
|
|
case response.status === 429:
|
|
throw new SsrHttpRateLimitError(response);
|
|
case response.status >= 400 && response.status < 500:
|
|
throw new SsrHttpClientError(response);
|
|
case response.status >= 500:
|
|
throw new SsrHttpServerError(response);
|
|
default:
|
|
throw new SsrHttpResponseError(response);
|
|
}
|
|
};
|
|
|
|
const getOptionsWithCacheKey = (url, options, cacheType = null) => {
|
|
if (options && options.cacheKey) {
|
|
if (!options.cacheTtl) options.cacheTtl = MINUTE;
|
|
|
|
return options;
|
|
}
|
|
|
|
if (options && options.method && options.method.toLowerCase() !== "get") {
|
|
delete options.cacheKey;
|
|
|
|
delete options.cacheTtl;
|
|
|
|
return options;
|
|
}
|
|
|
|
const newOptions = options ? { ...options } : {};
|
|
|
|
if (!newOptions || !newOptions.hasOwnProperty("cacheTtl")) {
|
|
newOptions.cacheTtl = MINUTE;
|
|
}
|
|
|
|
return {
|
|
...newOptions,
|
|
cacheKey: `${cacheType ? cacheType + ":" : ""}${url}`,
|
|
};
|
|
};
|
|
|
|
const setCacheIfNeeded = (response, cacheKey, cacheTtl) => {
|
|
if (cacheKey && cacheTtl) networkCache.set(cacheKey, response, cacheTtl);
|
|
|
|
return { ...response, cached: false };
|
|
};
|
|
|
|
export async function fetchUrl(url, options = {}, cors = true) {
|
|
try {
|
|
const response = await fetch(url, {
|
|
...options,
|
|
headers: {
|
|
"x-requested-with": ssrConfig.name,
|
|
},
|
|
...(cors ? { mode: "cors" } : null),
|
|
});
|
|
|
|
return checkResponse(response);
|
|
} catch (err) {
|
|
if (err instanceof TypeError) throw new SsrNetworkError("Network error");
|
|
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
export async function fetchJson(
|
|
url,
|
|
{ cacheTtl = null, maxAge = null, ...restOptions } = {}
|
|
) {
|
|
const options = getOptionsWithCacheKey(
|
|
url,
|
|
{ cacheTtl, maxAge, ...restOptions },
|
|
"json"
|
|
);
|
|
|
|
const {
|
|
cacheKey: fetchCacheKey,
|
|
cacheTtl: fetchCacheTtl,
|
|
maxAge: fetchMaxAge,
|
|
...fetchOptions
|
|
} = getOptionsWithCacheKey(url, options, "json");
|
|
|
|
if (fetchCacheKey && fetchCacheTtl) {
|
|
const cachedResponse = networkCache.get(fetchCacheKey, fetchMaxAge);
|
|
if (cachedResponse !== undefined)
|
|
return { ...cachedResponse, cached: true };
|
|
}
|
|
|
|
return fetchUrl(url, fetchOptions)
|
|
.then(async (response) => {
|
|
const body = await response.json();
|
|
|
|
return setCacheIfNeeded(
|
|
{
|
|
headers: response.headers,
|
|
rateLimit: parseRateLimitHeaders(response),
|
|
body,
|
|
},
|
|
fetchCacheKey,
|
|
fetchCacheTtl
|
|
);
|
|
})
|
|
.catch((err) => {
|
|
throw err instanceof SyntaxError
|
|
? new SsrDataFormatError("JSON parse error", err)
|
|
: err;
|
|
});
|
|
}
|
|
|
|
export async function fetchHtml(
|
|
url,
|
|
{ cacheTtl = null, maxAge = null, ...restOptions } = {}
|
|
) {
|
|
const options = getOptionsWithCacheKey(
|
|
url,
|
|
{ cacheTtl, maxAge, ...restOptions },
|
|
"json"
|
|
);
|
|
|
|
const {
|
|
cacheKey: fetchCacheKey,
|
|
cacheTtl: fetchCacheTtl,
|
|
maxAge: fetchMaxAge,
|
|
...fetchOptions
|
|
} = getOptionsWithCacheKey(url, options, "html");
|
|
|
|
if (fetchCacheKey && fetchCacheTtl) {
|
|
const cachedResponse = networkCache.get(fetchCacheKey, fetchMaxAge);
|
|
if (cachedResponse !== undefined)
|
|
return { ...cachedResponse, cached: true };
|
|
}
|
|
|
|
return fetchUrl(url, fetchOptions).then(async (response) => {
|
|
const body = await response.text();
|
|
|
|
return setCacheIfNeeded(
|
|
{
|
|
headers: response.headers,
|
|
rateLimit: parseRateLimitHeaders(response),
|
|
body: new DOMParser().parseFromString(body, "text/html"),
|
|
},
|
|
fetchCacheKey,
|
|
fetchCacheTtl
|
|
);
|
|
});
|
|
}
|