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:
@@ -1,3 +1,4 @@
|
|||||||
|
import { fetchWithTimeout } from "@homarr/common";
|
||||||
import type { z } from "@homarr/validation";
|
import type { z } from "@homarr/validation";
|
||||||
import { validation } from "@homarr/validation";
|
import { validation } from "@homarr/validation";
|
||||||
|
|
||||||
@@ -8,7 +9,7 @@ export const locationRouter = createTRPCRouter({
|
|||||||
.input(validation.location.searchCity.input)
|
.input(validation.location.searchCity.input)
|
||||||
.output(validation.location.searchCity.output)
|
.output(validation.location.searchCity.output)
|
||||||
.query(async ({ input }) => {
|
.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>;
|
return (await res.json()) as z.infer<typeof validation.location.searchCity.output>;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
import { fetchWithTimeout } from "@homarr/common";
|
||||||
import { validation } from "@homarr/validation";
|
import { validation } from "@homarr/validation";
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||||
|
|
||||||
export const weatherRouter = createTRPCRouter({
|
export const weatherRouter = createTRPCRouter({
|
||||||
atLocation: publicProcedure.input(validation.widget.weather.atLocationInput).query(async ({ input }) => {
|
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¤t_weather=true&timezone=auto`,
|
`https://api.open-meteo.com/v1/forecast?latitude=${input.latitude}&longitude=${input.longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min¤t_weather=true&timezone=auto`,
|
||||||
);
|
);
|
||||||
const json: unknown = await res.json();
|
const json: unknown = await res.json();
|
||||||
|
|||||||
16
packages/common/src/fetch-with-timeout.ts
Normal file
16
packages/common/src/fetch-with-timeout.ts
Normal 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);
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -8,3 +8,4 @@ export * from "./url";
|
|||||||
export * from "./number";
|
export * from "./number";
|
||||||
export * from "./error";
|
export * from "./error";
|
||||||
export * from "./encryption";
|
export * from "./encryption";
|
||||||
|
export * from "./fetch-with-timeout";
|
||||||
|
|||||||
@@ -21,7 +21,8 @@
|
|||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/log": "workspace:^0.1.0"
|
"@homarr/log": "workspace:^0.1.0",
|
||||||
|
"@homarr/common": "workspace:^0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { fetchWithTimeout } from "@homarr/common";
|
||||||
|
|
||||||
import type { IconRepositoryLicense } from "../types/icon-repository-license";
|
import type { IconRepositoryLicense } from "../types/icon-repository-license";
|
||||||
import type { RepositoryIconGroup } from "../types/repository-icon-group";
|
import type { RepositoryIconGroup } from "../types/repository-icon-group";
|
||||||
import { IconRepository } from "./icon-repository";
|
import { IconRepository } from "./icon-repository";
|
||||||
@@ -19,7 +21,7 @@ export class GitHubIconRepository extends IconRepository {
|
|||||||
throw new Error("Repository URLs are required for this repository");
|
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;
|
const listOfFiles = (await response.json()) as GitHubApiResponse;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { fetchWithTimeout } from "@homarr/common";
|
||||||
|
|
||||||
import type { IconRepositoryLicense } from "../types/icon-repository-license";
|
import type { IconRepositoryLicense } from "../types/icon-repository-license";
|
||||||
import type { RepositoryIconGroup } from "../types/repository-icon-group";
|
import type { RepositoryIconGroup } from "../types/repository-icon-group";
|
||||||
import { IconRepository } from "./icon-repository";
|
import { IconRepository } from "./icon-repository";
|
||||||
@@ -15,7 +17,7 @@ export class JsdelivrIconRepository extends IconRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async getAllIconsInternalAsync(): Promise<RepositoryIconGroup> {
|
protected async getAllIconsInternalAsync(): Promise<RepositoryIconGroup> {
|
||||||
const response = await fetch(this.repositoryIndexingUrl);
|
const response = await fetchWithTimeout(this.repositoryIndexingUrl);
|
||||||
const listOfFiles = (await response.json()) as JsdelivrApiResponse;
|
const listOfFiles = (await response.json()) as JsdelivrApiResponse;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { extractErrorMessage } from "@homarr/common";
|
import { extractErrorMessage, fetchWithTimeout } from "@homarr/common";
|
||||||
import { logger } from "@homarr/log";
|
import { logger } from "@homarr/log";
|
||||||
|
|
||||||
export const sendPingRequestAsync = async (url: string) => {
|
export const sendPingRequestAsync = async (url: string) => {
|
||||||
try {
|
try {
|
||||||
return await fetch(url).then((response) => ({ statusCode: response.status }));
|
return await fetchWithTimeout(url).then((response) => ({ statusCode: response.status }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("packages/ping/src/index.ts:", error);
|
logger.error("packages/ping/src/index.ts:", error);
|
||||||
return {
|
return {
|
||||||
|
|||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -868,6 +868,9 @@ importers:
|
|||||||
|
|
||||||
packages/icons:
|
packages/icons:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@homarr/common':
|
||||||
|
specifier: workspace:^0.1.0
|
||||||
|
version: link:../common
|
||||||
'@homarr/log':
|
'@homarr/log':
|
||||||
specifier: workspace:^0.1.0
|
specifier: workspace:^0.1.0
|
||||||
version: link:../log
|
version: link:../log
|
||||||
|
|||||||
Reference in New Issue
Block a user