fix: fetch timeout for external requests to small (#881)

* fix: fetch timeout for external requests to small

* fix: format issue

* fix: move clear timeout for fetch to finally
This commit is contained in:
Meier Lukas
2024-07-29 06:30:56 +02:00
committed by GitHub
parent 406ed16ce4
commit 67dad45214
9 changed files with 34 additions and 7 deletions

View File

@@ -1,3 +1,4 @@
import { fetchWithTimeout } from "@homarr/common";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
@@ -8,7 +9,7 @@ export const locationRouter = createTRPCRouter({
.input(validation.location.searchCity.input)
.output(validation.location.searchCity.output)
.query(async ({ input }) => {
const res = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${input.query}`);
const res = await fetchWithTimeout(`https://geocoding-api.open-meteo.com/v1/search?name=${input.query}`);
return (await res.json()) as z.infer<typeof validation.location.searchCity.output>;
}),
});

View File

@@ -1,10 +1,11 @@
import { fetchWithTimeout } from "@homarr/common";
import { validation } from "@homarr/validation";
import { createTRPCRouter, publicProcedure } from "../../trpc";
export const weatherRouter = createTRPCRouter({
atLocation: publicProcedure.input(validation.widget.weather.atLocationInput).query(async ({ input }) => {
const res = await fetch(
const res = await fetchWithTimeout(
`https://api.open-meteo.com/v1/forecast?latitude=${input.latitude}&longitude=${input.longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min&current_weather=true&timezone=auto`,
);
const json: unknown = await res.json();

View File

@@ -0,0 +1,16 @@
/**
* 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);
});
};

View File

@@ -8,3 +8,4 @@ export * from "./url";
export * from "./number";
export * from "./error";
export * from "./encryption";
export * from "./fetch-with-timeout";

View File

@@ -21,7 +21,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@homarr/log": "workspace:^0.1.0"
"@homarr/log": "workspace:^0.1.0",
"@homarr/common": "workspace:^0.1.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",

View File

@@ -1,3 +1,5 @@
import { fetchWithTimeout } from "@homarr/common";
import type { IconRepositoryLicense } from "../types/icon-repository-license";
import type { RepositoryIconGroup } from "../types/repository-icon-group";
import { IconRepository } from "./icon-repository";
@@ -19,7 +21,7 @@ export class GitHubIconRepository extends IconRepository {
throw new Error("Repository URLs are required for this repository");
}
const response = await fetch(this.repositoryIndexingUrl);
const response = await fetchWithTimeout(this.repositoryIndexingUrl);
const listOfFiles = (await response.json()) as GitHubApiResponse;
return {

View File

@@ -1,3 +1,5 @@
import { fetchWithTimeout } from "@homarr/common";
import type { IconRepositoryLicense } from "../types/icon-repository-license";
import type { RepositoryIconGroup } from "../types/repository-icon-group";
import { IconRepository } from "./icon-repository";
@@ -15,7 +17,7 @@ export class JsdelivrIconRepository extends IconRepository {
}
protected async getAllIconsInternalAsync(): Promise<RepositoryIconGroup> {
const response = await fetch(this.repositoryIndexingUrl);
const response = await fetchWithTimeout(this.repositoryIndexingUrl);
const listOfFiles = (await response.json()) as JsdelivrApiResponse;
return {

View File

@@ -1,9 +1,9 @@
import { extractErrorMessage } from "@homarr/common";
import { extractErrorMessage, fetchWithTimeout } from "@homarr/common";
import { logger } from "@homarr/log";
export const sendPingRequestAsync = async (url: string) => {
try {
return await fetch(url).then((response) => ({ statusCode: response.status }));
return await fetchWithTimeout(url).then((response) => ({ statusCode: response.status }));
} catch (error) {
logger.error("packages/ping/src/index.ts:", error);
return {

3
pnpm-lock.yaml generated
View File

@@ -868,6 +868,9 @@ importers:
packages/icons:
dependencies:
'@homarr/common':
specifier: workspace:^0.1.0
version: link:../common
'@homarr/log':
specifier: workspace:^0.1.0
version: link:../log