refactor(http): move to core package (#4690)
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import type { Logger } from "@homarr/core/infrastructure/logs";
|
||||
import type { ILogger } from "@homarr/core/infrastructure/logs";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
|
||||
import type { AnyRequestError } from "../request-error";
|
||||
import type { ResponseError } from "../response-error";
|
||||
|
||||
export abstract class HttpErrorHandler {
|
||||
protected logger: Logger;
|
||||
protected logger: ILogger;
|
||||
|
||||
constructor(type: string) {
|
||||
this.logger = createLogger({ module: "httpErrorHandler", type });
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Logger } from "@homarr/core/infrastructure/logs";
|
||||
import type { ILogger } from "@homarr/core/infrastructure/logs";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
|
||||
import type { ParseError } from "../parse-error";
|
||||
|
||||
export abstract class ParseErrorHandler {
|
||||
protected logger: Logger;
|
||||
protected logger: ILogger;
|
||||
constructor(type: string) {
|
||||
this.logger = createLogger({ module: "parseErrorHandler", type });
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import type { Dispatcher } from "undici";
|
||||
import { Agent } from "undici";
|
||||
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
|
||||
// The below import statement initializes dns-caching
|
||||
import "@homarr/core/infrastructure/dns/init";
|
||||
|
||||
const logger = createLogger({ module: "fetchAgent" });
|
||||
|
||||
export class LoggingAgent extends Agent {
|
||||
constructor(...props: ConstructorParameters<typeof Agent>) {
|
||||
super(...props);
|
||||
}
|
||||
|
||||
dispatch(options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean {
|
||||
const path = options.path
|
||||
.split("/")
|
||||
.map((segment) => (segment.length >= 32 && !segment.startsWith("?") ? "REDACTED" : segment))
|
||||
.join("/");
|
||||
const url = new URL(`${options.origin as string}${path}`);
|
||||
|
||||
// The below code should prevent sensitive data from being logged as
|
||||
// some integrations use query parameters for auth
|
||||
url.searchParams.forEach((value, key) => {
|
||||
if (value === "") return; // Skip empty values
|
||||
if (/^-?\d{1,12}$/.test(value)) return; // Skip small numbers
|
||||
if (value === "true" || value === "false") return; // Skip boolean values
|
||||
if (/^[a-zA-Z]{1,12}$/.test(value)) return; // Skip short strings
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return; // Skip dates
|
||||
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value)) return; // Skip date times
|
||||
|
||||
url.searchParams.set(key, "REDACTED");
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
`Dispatching request ${url.toString().replaceAll("=&", "&")} (${Object.keys(options.headers ?? {}).length} headers)`,
|
||||
);
|
||||
return super.dispatch(options, handler);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Same as fetch, but with a timeout of 10 seconds.
|
||||
* https://stackoverflow.com/questions/46946380/fetch-api-request-timeout
|
||||
* @param param0 fetch arguments
|
||||
* @returns fetch response
|
||||
*/
|
||||
export const fetchWithTimeout = (...[url, requestInit]: Parameters<typeof fetch>) => {
|
||||
const controller = new AbortController();
|
||||
|
||||
// 10 seconds timeout:
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
||||
|
||||
return fetch(url, { signal: controller.signal, ...requestInit }).finally(() => {
|
||||
clearTimeout(timeoutId);
|
||||
});
|
||||
};
|
||||
@@ -9,7 +9,6 @@ export * from "./id";
|
||||
export * from "./url";
|
||||
export * from "./number";
|
||||
export * from "./error";
|
||||
export * from "./fetch-with-timeout";
|
||||
export * from "./theme";
|
||||
export * from "./function";
|
||||
export * from "./id";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from "./security";
|
||||
export * from "./encryption";
|
||||
export * from "./user-agent";
|
||||
export * from "./fetch-agent";
|
||||
export * from "./errors";
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import type { Dispatcher } from "undici";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
import * as logs from "@homarr/core/infrastructure/logs";
|
||||
|
||||
import { LoggingAgent } from "../fetch-agent";
|
||||
|
||||
vi.mock("undici", () => {
|
||||
return {
|
||||
Agent: class Agent {
|
||||
dispatch(_options: Dispatcher.DispatchOptions, _handler: Dispatcher.DispatchHandler): boolean {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
setGlobalDispatcher: () => undefined,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@homarr/core/infrastructure/logs", async () => {
|
||||
const actual: typeof logs = await vi.importActual("@homarr/core/infrastructure/logs");
|
||||
return {
|
||||
...actual,
|
||||
createLogger: vi.fn().mockReturnValue({
|
||||
debug: vi.fn(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const REDACTED = "REDACTED";
|
||||
|
||||
const loggerMock = logs.createLogger({ module: "test" });
|
||||
|
||||
describe("LoggingAgent should log all requests", () => {
|
||||
test("should log all requests", () => {
|
||||
// Arrange
|
||||
const debugSpy = vi.spyOn(loggerMock, "debug");
|
||||
const agent = new LoggingAgent();
|
||||
|
||||
// Act
|
||||
agent.dispatch({ origin: "https://homarr.dev", path: "/", method: "GET" }, {});
|
||||
|
||||
// Assert
|
||||
expect(debugSpy).toHaveBeenCalledWith("Dispatching request https://homarr.dev/ (0 headers)");
|
||||
});
|
||||
|
||||
test("should show amount of headers", () => {
|
||||
// Arrange
|
||||
const debugSpy = vi.spyOn(loggerMock, "debug");
|
||||
const agent = new LoggingAgent();
|
||||
|
||||
// Act
|
||||
agent.dispatch(
|
||||
{
|
||||
origin: "https://homarr.dev",
|
||||
path: "/",
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "text/html",
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
},
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(debugSpy).toHaveBeenCalledWith(expect.stringContaining("(2 headers)"));
|
||||
});
|
||||
|
||||
test.each([
|
||||
["/?hex=a3815e8ada2ef9a31", `/?hex=${REDACTED}`],
|
||||
["/?uuid=f7c3f65e-c511-4f90-ba9a-3fd31418bd49", `/?uuid=${REDACTED}`],
|
||||
["/?password=complexPassword123", `/?password=${REDACTED}`],
|
||||
[
|
||||
// JWT for John Doe
|
||||
"/?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
|
||||
`/?jwt=${REDACTED}`,
|
||||
],
|
||||
["/?one=a1&two=b2&three=c3", `/?one=${REDACTED}&two=${REDACTED}&three=${REDACTED}`],
|
||||
["/?numberWith13Chars=1234567890123", `/?numberWith13Chars=${REDACTED}`],
|
||||
[`/?stringWith13Chars=${"a".repeat(13)}`, `/?stringWith13Chars=${REDACTED}`],
|
||||
[`/${"a".repeat(32)}/?param=123`, `/${REDACTED}/?param=123`],
|
||||
])("should redact sensitive data in url https://homarr.dev%s", (path, expected) => {
|
||||
// Arrange
|
||||
const debugSpy = vi.spyOn(loggerMock, "debug");
|
||||
const agent = new LoggingAgent();
|
||||
|
||||
// Act
|
||||
agent.dispatch({ origin: "https://homarr.dev", path, method: "GET" }, {});
|
||||
|
||||
// Assert
|
||||
expect(debugSpy).toHaveBeenCalledWith(expect.stringContaining(` https://homarr.dev${expected} `));
|
||||
});
|
||||
test.each([
|
||||
["empty", "/?empty"],
|
||||
["numbers with max 12 chars", "/?number=123456789012"],
|
||||
["true", "/?true=true"],
|
||||
["false", "/?false=false"],
|
||||
["strings with max 12 chars", `/?short=${"a".repeat(12)}`],
|
||||
["dates", "/?date=2022-01-01"],
|
||||
["date times", "/?datetime=2022-01-01T00:00:00.000Z"],
|
||||
])("should not redact values that are %s", (_reason, path) => {
|
||||
// Arrange
|
||||
const debugSpy = vi.spyOn(loggerMock, "debug");
|
||||
const agent = new LoggingAgent();
|
||||
|
||||
// Act
|
||||
agent.dispatch({ origin: "https://homarr.dev", path, method: "GET" }, {});
|
||||
|
||||
// Assert
|
||||
expect(debugSpy).toHaveBeenCalledWith(expect.stringContaining(` https://homarr.dev${path} `));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user