diff --git a/src/components/Dashboard/Tiles/Apps/AppPing.tsx b/src/components/Dashboard/Tiles/Apps/AppPing.tsx index b2e3bd559..5af4b0938 100644 --- a/src/components/Dashboard/Tiles/Apps/AppPing.tsx +++ b/src/components/Dashboard/Tiles/Apps/AppPing.tsx @@ -1,10 +1,10 @@ import { Indicator, Tooltip } from '@mantine/core'; import Consola from 'consola'; -import { useQuery } from '@tanstack/react-query'; import { motion } from 'framer-motion'; import { useTranslation } from 'next-i18next'; import { useConfigContext } from '../../../../config/provider'; import { AppType } from '../../../../types/app'; +import { api } from '~/utils/api'; interface AppPingProps { app: AppType; @@ -16,18 +16,7 @@ export const AppPing = ({ app }: AppPingProps) => { const active = (config?.settings.customization.layout.enabledPing && app.network.enabledStatusChecker) ?? false; - const { data, isLoading } = useQuery({ - queryKey: ['ping', { id: app.id, name: app.name }], - queryFn: async () => { - const response = await fetch(`/api/modules/ping?url=${encodeURI(app.url)}`); - const isOk = getIsOk(app, response.status); - return { - status: response.status, - state: isOk ? 'online' : 'down', - }; - }, - enabled: active, - }); + const { data, isLoading, error } = usePingQuery(app, active); const isOnline = data?.state === 'online'; @@ -49,7 +38,7 @@ export const AppPing = ({ app }: AppPingProps) => { ? t('states.loading') : isOnline ? t('states.online', { response: data.status }) - : t('states.offline', { response: data?.status }) + : t('states.offline', { response: data?.status ?? error?.data?.httpStatus }) } > { ); }; +const usePingQuery = (app: AppType, isEnabled: boolean) => + api.app.ping.useQuery( + { + url: app.url, + }, + { + enabled: isEnabled, + select: (data) => { + const statusCode = data.status; + const isOk = getIsOk(app, statusCode); + return { + status: statusCode, + state: isOk ? ('online' as const) : ('down' as const), + }; + }, + } + ); + const getIsOk = (app: AppType, status: number) => { -if (app.network.okStatus === undefined || app.network.statusCodes.length >= 1) { -Consola.log('Using new status codes'); -return app.network.statusCodes.includes(status.toString()); -} -Consola.warn('Using deprecated okStatus'); -return app.network.okStatus.includes(status); + if (app.network.okStatus === undefined || app.network.statusCodes.length >= 1) { + Consola.log('Using new status codes'); + return app.network.statusCodes.includes(status.toString()); + } + Consola.warn('Using deprecated okStatus'); + return app.network.okStatus.includes(status); }; diff --git a/src/modules/index.ts b/src/modules/index.ts index 3f69cd0de..54bebdbc1 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -1,2 +1 @@ -export * from './ping'; export * from './overseerr'; diff --git a/src/server/api/root.ts b/src/server/api/root.ts index 57f7f9f98..cf28aaa48 100644 --- a/src/server/api/root.ts +++ b/src/server/api/root.ts @@ -1,11 +1,14 @@ import { createTRPCRouter } from '~/server/api/trpc'; +import { appRouter } from './routers/app'; /** * This is the primary router for your server. * * All routers added in /api/routers should be manually added here. */ -export const rootRouter = createTRPCRouter({}); +export const rootRouter = createTRPCRouter({ + app: appRouter, +}); // export type definition of API export type RootRouter = typeof rootRouter; diff --git a/src/server/api/routers/app.ts b/src/server/api/routers/app.ts new file mode 100644 index 000000000..184e4989e --- /dev/null +++ b/src/server/api/routers/app.ts @@ -0,0 +1,46 @@ +import { z } from 'zod'; +import axios, { AxiosError } from 'axios'; +import https from 'https'; +import Consola from 'consola'; +import { TRPCError } from '@trpc/server'; +import { createTRPCRouter, publicProcedure } from '../trpc'; + +export const appRouter = createTRPCRouter({ + ping: publicProcedure + .input( + z.object({ + url: z.string(), + }) + ) + .query(async ({ input }) => { + const agent = new https.Agent({ rejectUnauthorized: false }); + const res = await axios + .get(input.url, { httpsAgent: agent, timeout: 2000 }) + .then((response) => ({ + status: response.status, + statusText: response.statusText, + })) + .catch((error: AxiosError) => { + if (error.response) { + Consola.warn(`Unexpected response: ${error.message}`); + return { + status: error.response.status, + statusText: error.response.statusText, + }; + } + if (error.code === 'ECONNABORTED') { + throw new TRPCError({ + code: 'TIMEOUT', + message: 'Request Timeout', + }); + } + + Consola.error(`Unexpected error: ${error.message}`); + throw new TRPCError({ + code: 'INTERNAL_SERVER_ERROR', + message: 'Internal Server Error', + }); + }); + return res; + }), +});