This repository has been archived on 2023-10-27. You can view files and clone it, but cannot push or open issues or pull requests.
Files
scoresaber-reloaded/src/network/fetch.js

179 lines
4.5 KiB
JavaScript
Raw Normal View History

2023-10-17 23:30:42 +01:00
import { SsrDataFormatError } from "../others/errors";
import ssrConfig from "../ssr-config";
import { MINUTE } from "../utils/date";
import createNetworkCache from "./cache";
2023-10-17 21:42:37 +01:00
import {
SsrHttpClientError,
SsrHttpNotFoundError,
SsrHttpRateLimitError,
SsrHttpResponseError,
2023-10-17 23:30:42 +01:00
SsrHttpServerError,
SsrHttpUnauthenticatedError,
2023-10-17 21:42:37 +01:00
SsrHttpUnauthorizedError,
SsrHttpUnprocessableEntityError,
SsrNetworkError,
2023-10-17 23:30:42 +01:00
} from "./errors";
import { parseRateLimitHeaders } from "./utils";
2023-10-17 21:42:37 +01:00
const networkCache = createNetworkCache();
2023-10-17 23:30:42 +01:00
const checkResponse = (response) => {
2023-10-17 21:42:37 +01:00
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);
}
2023-10-17 23:30:42 +01:00
};
2023-10-17 21:42:37 +01:00
const getOptionsWithCacheKey = (url, options, cacheType = null) => {
if (options && options.cacheKey) {
if (!options.cacheTtl) options.cacheTtl = MINUTE;
return options;
}
2023-10-17 23:30:42 +01:00
if (options && options.method && options.method.toLowerCase() !== "get") {
2023-10-17 21:42:37 +01:00
delete options.cacheKey;
delete options.cacheTtl;
return options;
}
2023-10-17 23:30:42 +01:00
const newOptions = options ? { ...options } : {};
2023-10-17 21:42:37 +01:00
2023-10-17 23:30:42 +01:00
if (!newOptions || !newOptions.hasOwnProperty("cacheTtl")) {
2023-10-17 21:42:37 +01:00
newOptions.cacheTtl = MINUTE;
}
2023-10-17 23:30:42 +01:00
return {
...newOptions,
cacheKey: `${cacheType ? cacheType + ":" : ""}${url}`,
};
};
2023-10-17 21:42:37 +01:00
const setCacheIfNeeded = (response, cacheKey, cacheTtl) => {
if (cacheKey && cacheTtl) networkCache.set(cacheKey, response, cacheTtl);
2023-10-17 23:30:42 +01:00
return { ...response, cached: false };
};
2023-10-17 21:42:37 +01:00
export async function fetchUrl(url, options = {}, cors = true) {
try {
2023-10-17 23:30:42 +01:00
console.log(ssrConfig.name);
const response = await fetch(url, {
...options,
headers: {
"x-requested-with": ssrConfig.name,
},
...(cors ? { mode: "cors" } : null),
});
2023-10-17 21:42:37 +01:00
return checkResponse(response);
} catch (err) {
2023-10-17 23:30:42 +01:00
if (err instanceof TypeError) throw new SsrNetworkError("Network error");
2023-10-17 21:42:37 +01:00
throw err;
}
}
2023-10-17 23:30:42 +01:00
export async function fetchJson(
url,
2023-10-17 23:38:18 +01:00
{ cacheTtl = null, maxAge = null, ...restOptions } = {},
2023-10-17 23:30:42 +01:00
) {
const options = getOptionsWithCacheKey(
url,
{ cacheTtl, maxAge, ...restOptions },
2023-10-17 23:38:18 +01:00
"json",
2023-10-17 23:30:42 +01:00
);
const {
cacheKey: fetchCacheKey,
cacheTtl: fetchCacheTtl,
maxAge: fetchMaxAge,
...fetchOptions
} = getOptionsWithCacheKey(url, options, "json");
2023-10-17 21:42:37 +01:00
if (fetchCacheKey && fetchCacheTtl) {
const cachedResponse = networkCache.get(fetchCacheKey, fetchMaxAge);
2023-10-17 23:30:42 +01:00
if (cachedResponse !== undefined)
return { ...cachedResponse, cached: true };
2023-10-17 21:42:37 +01:00
}
return fetchUrl(url, fetchOptions)
2023-10-17 23:30:42 +01:00
.then(async (response) => {
2023-10-17 21:42:37 +01:00
const body = await response.json();
2023-10-17 23:30:42 +01:00
return setCacheIfNeeded(
{
headers: response.headers,
rateLimit: parseRateLimitHeaders(response),
body,
},
fetchCacheKey,
2023-10-17 23:38:18 +01:00
fetchCacheTtl,
2023-10-17 23:30:42 +01:00
);
2023-10-17 21:42:37 +01:00
})
2023-10-17 23:30:42 +01:00
.catch((err) => {
throw err instanceof SyntaxError
? new SsrDataFormatError("JSON parse error", err)
: err;
});
2023-10-17 21:42:37 +01:00
}
2023-10-17 23:30:42 +01:00
export async function fetchHtml(
url,
2023-10-17 23:38:18 +01:00
{ cacheTtl = null, maxAge = null, ...restOptions } = {},
2023-10-17 23:30:42 +01:00
) {
const options = getOptionsWithCacheKey(
url,
{ cacheTtl, maxAge, ...restOptions },
2023-10-17 23:38:18 +01:00
"json",
2023-10-17 23:30:42 +01:00
);
const {
cacheKey: fetchCacheKey,
cacheTtl: fetchCacheTtl,
maxAge: fetchMaxAge,
...fetchOptions
} = getOptionsWithCacheKey(url, options, "html");
2023-10-17 21:42:37 +01:00
if (fetchCacheKey && fetchCacheTtl) {
const cachedResponse = networkCache.get(fetchCacheKey, fetchMaxAge);
2023-10-17 23:30:42 +01:00
if (cachedResponse !== undefined)
return { ...cachedResponse, cached: true };
2023-10-17 21:42:37 +01:00
}
2023-10-17 23:30:42 +01:00
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,
2023-10-17 23:38:18 +01:00
fetchCacheTtl,
2023-10-17 23:30:42 +01:00
);
});
}