Run pre-commit hooks over existing codebase

Co-Authored-By: Ben Phelps <ben@phelps.io>
This commit is contained in:
shamoon
2023-10-17 23:26:55 -07:00
parent fa50bbad9c
commit 19c25713c4
387 changed files with 4785 additions and 4109 deletions

View File

@@ -5,7 +5,7 @@ export function formatApiCall(url, args) {
return args[key] || "";
};
return url.replace(/\/+$/, "").replace(find, replace).replace(find,replace);
return url.replace(/\/+$/, "").replace(find, replace).replace(find, replace);
}
function getURLSearchParams(widget, endpoint) {
@@ -57,8 +57,8 @@ export function jsonArrayFilter(data, filter) {
export function sanitizeErrorURL(errorURL) {
// Dont display sensitive params on frontend
const url = new URL(errorURL);
["apikey", "api_key", "token", "t"].forEach(key => {
if (url.searchParams.has(key)) url.searchParams.set(key, "***")
["apikey", "api_key", "token", "t"].forEach((key) => {
if (url.searchParams.has(key)) url.searchParams.set(key, "***");
});
return url.toString();
}
}

View File

@@ -28,17 +28,12 @@ export default async function credentialedProxyHandler(req, res, map) {
headers["X-CMC_PRO_API_KEY"] = `${widget.key}`;
} else if (widget.type === "gotify") {
headers["X-gotify-Key"] = `${widget.key}`;
} else if ([
"authentik",
"cloudflared",
"ghostfolio",
"mealie",
"tailscale",
"truenas",
"pterodactyl",
].includes(widget.type))
{
headers.Authorization = `Bearer ${widget.key}`;
} else if (
["authentik", "cloudflared", "ghostfolio", "mealie", "tailscale", "truenas", "pterodactyl"].includes(
widget.type,
)
) {
headers.Authorization = `Bearer ${widget.key}`;
} else if (widget.type === "proxmox") {
headers.Authorization = `PVEAPIToken=${widget.username}=${widget.password}`;
} else if (widget.type === "proxmoxbackupserver") {
@@ -62,8 +57,7 @@ export default async function credentialedProxyHandler(req, res, map) {
} else {
headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`;
}
}
else if (widget.type === "azuredevops") {
} else if (widget.type === "azuredevops") {
headers.Authorization = `Basic ${Buffer.from(`$:${widget.key}`).toString("base64")}`;
} else if (widget.type === "glances") {
headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`;
@@ -91,10 +85,12 @@ export default async function credentialedProxyHandler(req, res, map) {
if (status >= 400) {
logger.error("HTTP Error %d calling %s", status, url.toString());
}
if (status === 200) {
if (!validateWidgetData(widget, endpoint, resultData)) {
return res.status(500).json({error: {message: "Invalid data", url: sanitizeErrorURL(url), data: resultData}});
return res
.status(500)
.json({ error: { message: "Invalid data", url: sanitizeErrorURL(url), data: resultData } });
}
if (map) resultData = map(resultData);
}

View File

@@ -19,10 +19,12 @@ export default async function genericProxyHandler(req, res, map) {
if (widget) {
// if there are more than one question marks, replace others to &
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }).replace(/(?<=\?.*)\?/g, '&'));
const url = new URL(
formatApiCall(widgets[widget.type].api, { endpoint, ...widget }).replace(/(?<=\?.*)\?/g, "&"),
);
const headers = req.extraHeaders ?? widget.headers ?? {};
if (widget.username && widget.password) {
headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`;
}
@@ -30,7 +32,7 @@ export default async function genericProxyHandler(req, res, map) {
const params = {
method: widget.method ?? req.method,
headers,
}
};
if (req.body) {
params.body = req.body;
}
@@ -38,14 +40,16 @@ export default async function genericProxyHandler(req, res, map) {
const [status, contentType, data] = await httpProxy(url, params);
let resultData = data;
if (resultData.error?.url) {
resultData.error.url = sanitizeErrorURL(url);
}
if (status === 200) {
if (!validateWidgetData(widget, endpoint, resultData)) {
return res.status(status).json({error: {message: "Invalid data", url: sanitizeErrorURL(url), data: resultData}});
return res
.status(status)
.json({ error: { message: "Invalid data", url: sanitizeErrorURL(url), data: resultData } });
}
if (map) resultData = map(resultData);
}
@@ -62,10 +66,10 @@ export default async function genericProxyHandler(req, res, map) {
status,
url.protocol,
url.hostname,
url.port ? `:${url.port}` : '',
url.pathname
url.port ? `:${url.port}` : "",
url.pathname,
);
return res.status(status).json({error: {message: "HTTP Error", url: sanitizeErrorURL(url), resultData}});
return res.status(status).json({ error: { message: "HTTP Error", url: sanitizeErrorURL(url), resultData } });
}
return res.status(status).send(resultData);

View File

@@ -11,8 +11,8 @@ const logger = createLogger("jsonrpcProxyHandler");
export async function sendJsonRpcRequest(url, method, params, username, password) {
const headers = {
"content-type": "application/json",
"accept": "application/json"
}
accept: "application/json",
};
if (username && password) {
headers.authorization = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
@@ -23,7 +23,7 @@ export async function sendJsonRpcRequest(url, method, params, username, password
const httpRequestParams = {
method: "POST",
headers,
body
body,
};
// eslint-disable-next-line no-unused-vars
@@ -33,7 +33,7 @@ export async function sendJsonRpcRequest(url, method, params, username, password
// in order to get access to the underlying error object in the JSON response
// you must set `result` equal to undefined
if (json.error && (json.result === null)) {
if (json.error && json.result === null) {
json.result = undefined;
}
return client.receive(json);
@@ -45,15 +45,14 @@ export async function sendJsonRpcRequest(url, method, params, username, password
try {
const response = await client.request(method, params);
return [200, "application/json", JSON.stringify(response)];
}
catch (e) {
} catch (e) {
if (e instanceof JSONRPCErrorException) {
logger.debug("Error calling JSONPRC endpoint: %s. %s", url, e.message);
return [200, "application/json", JSON.stringify({result: null, error: {code: e.code, message: e.message}})];
return [200, "application/json", JSON.stringify({ result: null, error: { code: e.code, message: e.message } })];
}
logger.warn("Error calling JSONPRC endpoint: %s. %s", url, e);
return [500, "application/json", JSON.stringify({result: null, error: {code: 2, message: e.toString()}})];
return [500, "application/json", JSON.stringify({ result: null, error: { code: 2, message: e.toString() } })];
}
}

View File

@@ -7,7 +7,8 @@ import createLogger from "utils/logger";
import widgets from "widgets/widgets";
const INFO_ENDPOINT = "{url}/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query";
const AUTH_ENDPOINT = "{url}/webapi/{path}?api=SYNO.API.Auth&version={maxVersion}&method=login&account={username}&passwd={password}&session=DownloadStation&format=cookie";
const AUTH_ENDPOINT =
"{url}/webapi/{path}?api=SYNO.API.Auth&version={maxVersion}&method=login&account={username}&passwd={password}&session=DownloadStation&format=cookie";
const AUTH_API_NAME = "SYNO.API.Auth";
const proxyName = "synologyProxyHandler";
@@ -40,7 +41,7 @@ async function login(loginUrl) {
}
async function getApiInfo(serviceWidget, apiName, serviceName) {
const cacheKey = `${proxyName}__${apiName}__${serviceName}`
const cacheKey = `${proxyName}__${apiName}__${serviceName}`;
let { cgiPath, maxVersion } = cache.get(cacheKey) ?? {};
if (cgiPath && maxVersion) {
return [cgiPath, maxVersion];
@@ -56,12 +57,13 @@ async function getApiInfo(serviceWidget, apiName, serviceName) {
if (json?.data?.[apiName]) {
cgiPath = json.data[apiName].path;
maxVersion = json.data[apiName].maxVersion;
logger.debug(`Detected ${serviceWidget.type}: apiName '${apiName}', cgiPath '${cgiPath}', and maxVersion ${maxVersion}`);
logger.debug(
`Detected ${serviceWidget.type}: apiName '${apiName}', cgiPath '${cgiPath}', and maxVersion ${maxVersion}`,
);
cache.put(cacheKey, { cgiPath, maxVersion });
return [cgiPath, maxVersion];
}
}
catch {
} catch {
logger.warn(`Error ${status} obtaining ${apiName} info`);
}
}
@@ -124,7 +126,7 @@ function toError(url, synologyError) {
error.error = synologyError.message ?? "Unknown error.";
break;
}
logger.warn(`Unable to call ${url}. code: ${code}, error: ${error.error}.`)
logger.warn(`Unable to call ${url}. code: ${code}, error: ${error.error}.`);
return error;
}
@@ -144,7 +146,7 @@ export default async function synologyProxyHandler(req, res) {
const [cgiPath, maxVersion] = await getApiInfo(serviceWidget, mapping.apiName, service);
if (!cgiPath || !maxVersion) {
return res.status(400).json({ error: `Unrecognized API name: ${mapping.apiName}`})
return res.status(400).json({ error: `Unrecognized API name: ${mapping.apiName}` });
}
const url = formatApiCall(widget.api, {
@@ -152,7 +154,7 @@ export default async function synologyProxyHandler(req, res) {
apiMethod: mapping.apiMethod,
cgiPath,
maxVersion,
...serviceWidget
...serviceWidget,
});
let [status, contentType, data] = await httpProxy(url);
if (status !== 200) {

View File

@@ -25,21 +25,21 @@ function handleRequest(requestor, url, params) {
addCookieHandler(url, params);
if (params?.body) {
params.headers = params.headers ?? {};
params.headers['content-length'] = Buffer.byteLength(params.body);
params.headers["content-length"] = Buffer.byteLength(params.body);
}
const request = requestor.request(url, params, (response) => {
const data = [];
const contentEncoding = response.headers['content-encoding']?.trim().toLowerCase();
const contentEncoding = response.headers["content-encoding"]?.trim().toLowerCase();
let responseContent = response;
if (contentEncoding === 'gzip' || contentEncoding === 'deflate') {
if (contentEncoding === "gzip" || contentEncoding === "deflate") {
// https://github.com/request/request/blob/3c0cddc7c8eb60b470e9519da85896ed7ee0081e/request.js#L1018-L1025
// Be more lenient with decoding compressed responses, in case of invalid gzip responses that are still accepted
// by common browsers.
responseContent = createUnzip({
flush: zlibConstants.Z_SYNC_FLUSH,
finishFlush: zlibConstants.Z_SYNC_FLUSH
finishFlush: zlibConstants.Z_SYNC_FLUSH,
});
// zlib errors
@@ -100,14 +100,13 @@ export async function httpProxy(url, params = {}) {
try {
const [status, contentType, data, responseHeaders] = await request;
return [status, contentType, data, responseHeaders];
}
catch (err) {
} catch (err) {
logger.error(
"Error calling %s//%s%s%s...",
constructedUrl.protocol,
constructedUrl.hostname,
constructedUrl.port ? `:${constructedUrl.port}` : '',
constructedUrl.pathname
constructedUrl.port ? `:${constructedUrl.port}` : "",
constructedUrl.pathname,
);
logger.error(err);
return [500, "application/json", { error: { message: err?.message ?? "Unknown error", url, rawError: err } }, null];

View File

@@ -7,11 +7,11 @@ export default function useWidgetAPI(widget, ...options) {
if (options && options[1]?.refreshInterval) {
config.refreshInterval = options[1].refreshInterval;
}
let url = formatProxyUrl(widget, ...options)
let url = formatProxyUrl(widget, ...options);
if (options[0] === "") {
url = null
url = null;
}
const { data, error, mutate } = useSWR(url, config);
// make the data error the top-level error
return { data, error: data?.error ?? error, mutate }
return { data, error: data?.error ?? error, mutate };
}

View File

@@ -2,34 +2,38 @@
import widgets from "widgets/widgets";
export default function validateWidgetData(widget, endpoint, data) {
let valid = true;
let dataParsed = data;
let error;
let mapping;
if (Buffer.isBuffer(data)) {
try {
dataParsed = JSON.parse(data);
} catch (e) {
error = e;
valid = false;
}
let valid = true;
let dataParsed = data;
let error;
let mapping;
if (Buffer.isBuffer(data)) {
try {
dataParsed = JSON.parse(data);
} catch (e) {
error = e;
valid = false;
}
}
if (dataParsed && Object.entries(dataParsed).length) {
const mappings = widgets[widget.type]?.mappings;
if (mappings) {
mapping = Object.values(mappings).find(m => m.endpoint === endpoint);
mapping?.validate?.forEach(key => {
if (dataParsed[key] === undefined) {
valid = false;
}
});
if (dataParsed && Object.entries(dataParsed).length) {
const mappings = widgets[widget.type]?.mappings;
if (mappings) {
mapping = Object.values(mappings).find((m) => m.endpoint === endpoint);
mapping?.validate?.forEach((key) => {
if (dataParsed[key] === undefined) {
valid = false;
}
});
}
}
if (!valid) {
console.warn(`Invalid data for widget '${widget.type}' endpoint '${endpoint}':\nExpected:${mapping?.validate}\nParse error: ${error ?? "none"}\nData: ${JSON.stringify(data)}`);
}
return valid;
if (!valid) {
console.warn(
`Invalid data for widget '${widget.type}' endpoint '${endpoint}':\nExpected:${mapping?.validate}\nParse error: ${
error ?? "none"
}\nData: ${JSON.stringify(data)}`,
);
}
return valid;
}