From 20ccee1a6ff4f891c59ebd6ca73d39f05ca7c5f6 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Wed, 13 Mar 2024 15:31:44 +0530 Subject: [PATCH 1/4] :art: Improved error handling for serverside --- Taskfile.yaml | 2 +- lib/app-setup/root.tsx | 13 ++++-- lib/client/helpers/log.ts | 4 +- lib/client/hooks/use-custom-loader-data.tsx | 12 ++++++ .../helpers/execute-query-with-context.ts | 4 +- lib/utils/common.tsx | 24 +++++++++-- src/apps/auth/routes/_main+/logout.tsx | 41 +++++++++---------- src/apps/auth/routes/_main+/verify-email.tsx | 6 ++- 8 files changed, 71 insertions(+), 35 deletions(-) create mode 100644 lib/client/hooks/use-custom-loader-data.tsx diff --git a/Taskfile.yaml b/Taskfile.yaml index cc9bb95da..41360ec3b 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -8,7 +8,7 @@ tasks: interactive: true cmds: - | - BASE_URL=gcp-production.kloudlite.io + BASE_URL=dev.kloudlite.io COOKIE_DOMAIN=".kloudlite.io" GATEWAY_URL="http://gateway.kloudlite.svc.cluster.local" case {{.app}} in diff --git a/lib/app-setup/root.tsx b/lib/app-setup/root.tsx index 696bf1f5f..7c22f3087 100644 --- a/lib/app-setup/root.tsx +++ b/lib/app-setup/root.tsx @@ -32,6 +32,7 @@ import tailwindBase from '~/design-system/tailwind-base.js'; import { ReloadIndicator } from '~/lib/client/components/reload-indicator'; import { isDev } from '~/lib/client/helpers/log'; import { getClientEnv, getServerEnv } from '../configs/base-url.cjs'; +import { useDataFromMatches } from '../client/hooks/use-custom-matches'; export const links: LinksFunction = () => [ { rel: 'stylesheet', href: stylesUrl }, @@ -167,6 +168,8 @@ const Root = ({ }) => { const env = useLoaderData(); + const error = useDataFromMatches('error', ''); + return ( @@ -235,9 +238,13 @@ const Root = ({ - - - + {error ? ( +
{JSON.stringify(error)}
+ ) : ( + + + + )} diff --git a/lib/client/helpers/log.ts b/lib/client/helpers/log.ts index 0e18c6651..63ef9b663 100644 --- a/lib/client/helpers/log.ts +++ b/lib/client/helpers/log.ts @@ -90,12 +90,12 @@ const logger = { } if (err) { - console.log(err); + console.trace(`\n\n${err}\n\n`); if (!isDev) { PostToHook(`\`\`\`${err}\`\`\``); } } else { - console.trace(args); + console.trace(`\n\n${args}\n\n`); } if (isDev && typeof window === 'undefined') { diff --git a/lib/client/hooks/use-custom-loader-data.tsx b/lib/client/hooks/use-custom-loader-data.tsx new file mode 100644 index 000000000..75b2088be --- /dev/null +++ b/lib/client/hooks/use-custom-loader-data.tsx @@ -0,0 +1,12 @@ +import { useLoaderData } from '@remix-run/react'; + +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( + k: infer I +) => void + ? I + : never; + +const useExtLoaderData = Promise>() => + useLoaderData() as UnionToIntersection>>; + +export default useExtLoaderData; diff --git a/lib/server/helpers/execute-query-with-context.ts b/lib/server/helpers/execute-query-with-context.ts index 91f24e7da..43eea48a6 100644 --- a/lib/server/helpers/execute-query-with-context.ts +++ b/lib/server/helpers/execute-query-with-context.ts @@ -112,12 +112,12 @@ export const ExecuteQueryWithContext = ( return { ...resp.data, data }; } catch (err) { if ((err as AxiosError).response) { - console.trace('ErrorIn:', apiName, (err as Error).name); + console.log('\nErrorIn:', apiName, (err as Error).name, '\n'); return (err as AxiosError).response?.data; } - console.trace('ErrorIn:', apiName, (err as Error).message); + console.log('\nErrorIn:', apiName, (err as Error).message, '\n'); return { data: null, diff --git a/lib/utils/common.tsx b/lib/utils/common.tsx index fb71ad287..d731753fa 100644 --- a/lib/utils/common.tsx +++ b/lib/utils/common.tsx @@ -1,10 +1,27 @@ import { toast } from '~/components/molecule/toast'; import logger from '../client/helpers/log'; -export const handleError = (e: unknown): void => { +export const handleError = ( + e: unknown +): { + error?: { + message: string; + }; +} => { + if (typeof window === 'undefined') { + const a = e as Error; + return { + error: { + message: a.message, + }, + }; + } + const err = e as Error; toast.error(err.message); logger.error(e); + + return {}; }; export const parseError = (e: unknown): Error => { @@ -27,8 +44,9 @@ export const Truncate = ({ }; export function sleep(time: number) { - // eslint-disable-next-line no-promise-executor-return - return new Promise((resolve) => setTimeout(resolve, time)); + return new Promise((resolve) => { + setTimeout(resolve, time); + }); } export const anyUndefined: any = undefined; diff --git a/src/apps/auth/routes/_main+/logout.tsx b/src/apps/auth/routes/_main+/logout.tsx index 6d06933d7..9a34770b9 100644 --- a/src/apps/auth/routes/_main+/logout.tsx +++ b/src/apps/auth/routes/_main+/logout.tsx @@ -1,13 +1,27 @@ -import { getCookie } from '~/root/lib/app-setup/cookies'; -import withContext from '~/root/lib/app-setup/with-contxt'; -import { useNavigate, useLoaderData } from '@remix-run/react'; -import { useEffect } from 'react'; +import { useNavigate } from '@remix-run/react'; import { BrandLogo } from '~/components/branding/brand-logo'; import { IExtRemixCtx } from '~/root/lib/types/common'; +import { GQLServerHandler } from '~/auth/server/gql/saved-queries'; +import { handleError } from '~/root/lib/utils/common'; +import useExtLoaderData from '~/root/lib/client/hooks/use-custom-loader-data'; +import { useEffect } from 'react'; + +export const loader = async (ctx: IExtRemixCtx) => { + const { errors } = await GQLServerHandler(ctx.request).logout(); + + if (errors) { + return handleError(errors[0]); + } + + return { + done: true, + }; +}; const LogoutPage = () => { const navigate = useNavigate(); - const { done } = useLoaderData(); + + const { done } = useExtLoaderData(); useEffect(() => { if (done) { @@ -22,21 +36,4 @@ const LogoutPage = () => { ); }; -export const loader = async (ctx: IExtRemixCtx) => { - const cookie = getCookie(ctx); - - const keys = Object.keys(cookie.getAll()); - - for (let i = 0; i < keys.length; i += 1) { - const key = keys[i]; - if (key === 'hotspot-session') { - cookie.remove(key); - } - } - - return withContext(ctx, { - done: 'true', - }); -}; - export default LogoutPage; diff --git a/src/apps/auth/routes/_main+/verify-email.tsx b/src/apps/auth/routes/_main+/verify-email.tsx index c34b6e724..b85459f96 100644 --- a/src/apps/auth/routes/_main+/verify-email.tsx +++ b/src/apps/auth/routes/_main+/verify-email.tsx @@ -46,6 +46,7 @@ const VerifyEmail = () => { (async () => { try { if (!email) { + // TODO: handle this case, by taking email from user toast.error('Something went wrong! Please try again.'); return; } @@ -137,8 +138,9 @@ export const loader = async (ctx: IRemixCtx) => { const query = getQueries(ctx); const { data, errors } = await GQLServerHandler(ctx.request).whoAmI(); if (errors) { - console.error(errors[0].message); - return redirect('/'); + return { + query, + }; } const { email, verified } = data || {}; From 3597630126940152707ff7ad830244592013a325 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Wed, 13 Mar 2024 16:53:22 +0530 Subject: [PATCH 2/4] :bug: Fixed issue with logs, state [loading,subscribed] --- lib/client/components/logger/index.tsx | 4 +- lib/client/helpers/log.ts | 52 +++------------------ lib/client/helpers/socket/context.tsx | 24 ++++++---- lib/client/helpers/socket/useSockLogs.tsx | 14 +----- lib/client/hooks/use-custom-loader-data.tsx | 4 +- lib/utils/common.tsx | 6 +-- src/apps/auth/routes/_main+/logout.tsx | 47 ++++++++++--------- 7 files changed, 52 insertions(+), 99 deletions(-) diff --git a/lib/client/components/logger/index.tsx b/lib/client/components/logger/index.tsx index 85bd5c3ea..f85dd9a34 100644 --- a/lib/client/components/logger/index.tsx +++ b/lib/client/components/logger/index.tsx @@ -669,7 +669,7 @@ const LogComp = ({ } }, [fullScreen]); - const { logs, subscribed, errors } = useSocketLogs(websocket); + const { logs, subscribed, errors, isLoading } = useSocketLogs(websocket); const [isClientSide, setIsClientSide] = useState(false); @@ -744,7 +744,7 @@ const LogComp = ({ )} - {!subscribed && logs.length === 0 && } + {isLoading && } {errors.length ? (
{JSON.stringify(errors)}
diff --git a/lib/client/helpers/log.ts b/lib/client/helpers/log.ts index 63ef9b663..ed4525f45 100644 --- a/lib/client/helpers/log.ts +++ b/lib/client/helpers/log.ts @@ -1,7 +1,4 @@ -// import axios from 'axios'; -// import { consoleBaseUrl } from '../../configs/base-url.cjs'; import { serverError } from '../../server/helpers/server-error'; -// import { parseError } from '../../utils/common'; const getNodeEnv = () => { const env = (() => { @@ -19,43 +16,6 @@ const getNodeEnv = () => { return 'development'; }; -/* eslint-disable no-unused-vars */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -export const PostErr = async (message: string, source: string) => { - // try { - // await axios.post( - // 'https://hooks.slack.com/services/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', - // { - // body: { - // channel: source === 'server' ? '#bugs' : '#web-errors', - // username: - // typeof window === 'undefined' ? 'server-error' : 'web-error', - // text: message, - // icon_emoji: ':ghost:', - // }, - // } - // ); - // } catch (err) { - // console.log(parseError(err).message); - // } - return {}; -}; - -const PostToHook = (message: string) => { - // if (typeof window === 'undefined') { - // return PostErr(message, 'server'); - // } - // - // try { - // axios.post(`${consoleBaseUrl}/api/error`, { - // body: { error: message }, - // }); - // } catch (err) { - // console.log(err); - // } - return {}; -}; - export const isDev = getNodeEnv() === 'development'; const logger = { @@ -63,8 +23,6 @@ const logger = { timeEnd: isDev ? console.timeEnd : () => {}, log: isDev ? console.log : () => {}, - // log: console.log, - warn: console.warn, trace: (...args: any[]) => { let err; @@ -90,14 +48,16 @@ const logger = { } if (err) { - console.trace(`\n\n${err}\n\n`); if (!isDev) { - PostToHook(`\`\`\`${err}\`\`\``); + console.trace(`\n\n${args}\n\n`); + return; } - } else { - console.trace(`\n\n${args}\n\n`); + console.error(`\n\n${err}\n\n`); + return; } + console.trace(`\n\n${args}\n\n`); + if (isDev && typeof window === 'undefined') { serverError(args); } diff --git a/lib/client/helpers/socket/context.tsx b/lib/client/helpers/socket/context.tsx index e4d3859be..07c66fecb 100644 --- a/lib/client/helpers/socket/context.tsx +++ b/lib/client/helpers/socket/context.tsx @@ -62,8 +62,8 @@ export const useSubscribe = ( const { sendMsg, responses, - infos: i, - errors: e, + infos: mInfos, + errors: mErrors, clear, } = useContext(Context); @@ -85,9 +85,10 @@ export const useSubscribe = ( const m = msg[k]; tr.push(...(responses[m.for]?.[m.data.id || 'default'] || [])); - terr.push(...(e[m.for]?.[m.data.id || 'default'] || [])); - ti.push(...(i[m.for]?.[m.data.id || 'default'] || [])); + terr.push(...(mErrors[m.for]?.[m.data.id || 'default'] || [])); + ti.push(...(mInfos[m.for]?.[m.data.id || 'default'] || [])); } + setResp(tr); setErrors(terr); setInfos(ti); @@ -97,16 +98,19 @@ export const useSubscribe = ( } return; } + const tempResp = responses[msg.for]?.[msg.data.id || 'default'] || []; + setResp(tempResp); + + setErrors(mErrors[msg.for]?.[msg.data.id || 'default'] || []); - setResp(responses[msg.for]?.[msg.data.id || 'default'] || []); - setErrors(e[msg.for]?.[msg.data.id || 'default'] || []); - setInfos(i[msg.for]?.[msg.data.id || 'default'] || []); + const tempInfo = mInfos[msg.for]?.[msg.data.id || 'default'] || []; + setInfos(tempInfo); - if (resp.length || i[msg.for]?.[msg.data.id || 'default']?.length) { + if (tempResp.length || tempInfo.length) { setSubscribed(true); } })(); - }, [responses]); + }, [responses, mInfos, mErrors]); useDebounce( () => { @@ -236,7 +240,7 @@ export const SockProvider = ({ children }: ChildrenProps) => { }; w.onerror = (e) => { - console.error(e); + console.error('socket closed:', e); if (!rejected) { rejected = true; rej(e); diff --git a/lib/client/helpers/socket/useSockLogs.tsx b/lib/client/helpers/socket/useSockLogs.tsx index 81e28e5cc..b1831166f 100644 --- a/lib/client/helpers/socket/useSockLogs.tsx +++ b/lib/client/helpers/socket/useSockLogs.tsx @@ -11,7 +11,7 @@ interface IuseLog { export const useSocketLogs = ({ account, cluster, trackingId }: IuseLog) => { const [logs, setLogs] = useState[]>([]); - const { responses, subscribed, errors } = useSubscribe( + const { responses, infos, subscribed, errors } = useSubscribe( { for: 'logs', data: { @@ -26,16 +26,6 @@ export const useSocketLogs = ({ account, cluster, trackingId }: IuseLog) => { [] ); - const [isLoading, setIsLoading] = useState(true); - - useEffect(() => { - if (subscribed && isLoading) { - setIsLoading(false); - } else if (!subscribed && !isLoading) { - setIsLoading(true); - } - }, []); - useEffect(() => { const sorted = responses.sort((a, b) => { const resp = b.data.podName.localeCompare(a.data.podName); @@ -55,7 +45,7 @@ export const useSocketLogs = ({ account, cluster, trackingId }: IuseLog) => { return { logs, errors, - isLoading, + isLoading: !subscribed && (logs.length === 0 || infos.length === 0), subscribed, }; }; diff --git a/lib/client/hooks/use-custom-loader-data.tsx b/lib/client/hooks/use-custom-loader-data.tsx index 75b2088be..40ca49199 100644 --- a/lib/client/hooks/use-custom-loader-data.tsx +++ b/lib/client/hooks/use-custom-loader-data.tsx @@ -6,7 +6,5 @@ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( ? I : never; -const useExtLoaderData = Promise>() => +export const useExtLoaderData = Promise>() => useLoaderData() as UnionToIntersection>>; - -export default useExtLoaderData; diff --git a/lib/utils/common.tsx b/lib/utils/common.tsx index d731753fa..38469880d 100644 --- a/lib/utils/common.tsx +++ b/lib/utils/common.tsx @@ -8,16 +8,16 @@ export const handleError = ( message: string; }; } => { + const err = e as Error; + if (typeof window === 'undefined') { - const a = e as Error; return { error: { - message: a.message, + message: err.message, }, }; } - const err = e as Error; toast.error(err.message); logger.error(e); diff --git a/src/apps/auth/routes/_main+/logout.tsx b/src/apps/auth/routes/_main+/logout.tsx index 9a34770b9..ecea7e1a8 100644 --- a/src/apps/auth/routes/_main+/logout.tsx +++ b/src/apps/auth/routes/_main+/logout.tsx @@ -1,33 +1,34 @@ import { useNavigate } from '@remix-run/react'; import { BrandLogo } from '~/components/branding/brand-logo'; -import { IExtRemixCtx } from '~/root/lib/types/common'; -import { GQLServerHandler } from '~/auth/server/gql/saved-queries'; -import { handleError } from '~/root/lib/utils/common'; -import useExtLoaderData from '~/root/lib/client/hooks/use-custom-loader-data'; -import { useEffect } from 'react'; - -export const loader = async (ctx: IExtRemixCtx) => { - const { errors } = await GQLServerHandler(ctx.request).logout(); - - if (errors) { - return handleError(errors[0]); - } - - return { - done: true, - }; -}; +import { handleError, sleep } from '~/root/lib/utils/common'; +import { useAuthApi } from '~/auth/server/gql/api-provider'; +import { toast } from 'react-toastify'; +import useDebounce from '~/root/lib/client/hooks/use-debounce'; const LogoutPage = () => { const navigate = useNavigate(); + const api = useAuthApi(); - const { done } = useExtLoaderData(); + useDebounce( + () => { + (async () => { + try { + const { errors } = await api.logout({}); + if (errors) { + throw errors[0]; + } - useEffect(() => { - if (done) { - navigate('/'); - } - }, [done]); + toast.warn('Logged out successfully'); + await sleep(1000); + navigate('/login'); + } catch (error) { + handleError(error); + } + })(); + }, + 1000, + [] + ); return (
From 52c7368521502e535fa1fd440edce4c133c99f45 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 14 Mar 2024 12:02:49 +0530 Subject: [PATCH 3/4] :bug: Fixed issue with websocket reconnect --- lib/client/components/logger/index.tsx | 2 +- lib/client/helpers/socket/context.tsx | 66 ++++++++++++++++---------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/lib/client/components/logger/index.tsx b/lib/client/components/logger/index.tsx index f85dd9a34..bdce1fc41 100644 --- a/lib/client/components/logger/index.tsx +++ b/lib/client/components/logger/index.tsx @@ -747,7 +747,7 @@ const LogComp = ({ {isLoading && } {errors.length ? ( -
{JSON.stringify(errors)}
+
{JSON.stringify(errors, null, 2)}
) : ( logs.length > 0 && ( { useDebounce( () => { if (typeof window !== 'undefined') { - try { - sockPromise.current = new Promise((res, rej) => { - let rejected = false; - try { - // eslint-disable-next-line new-cap - const w = new wsock.w3cwebsocket(`${socketUrl}/ws`, '', '', {}); - - w.onmessage = onMessage; - - w.onopen = () => { - res(w); - }; - - w.onerror = (e) => { - console.error('socket closed:', e); - if (!rejected) { - rejected = true; + const connnect = (recon = () => {}) => { + try { + sockPromise.current = new Promise( + (res, rej) => { + try { + // eslint-disable-next-line new-cap + const w = new wsock.w3cwebsocket( + `${socketUrl}/ws`, + '', + '', + {} + ); + + w.onmessage = onMessage; + + w.onopen = () => { + res(w); + }; + + w.onerror = (e) => { + console.error(e); + recon(); + }; + + w.onclose = () => { + recon(); + }; + } catch (e) { rej(e); } - }; + } + ); + } catch (e) { + logger.error(e); + } + }; - w.onclose = () => {}; - } catch (e) { - rej(e); - } - }); - } catch (e) { - logger.error(e); - } + connnect(() => { + setTimeout(() => { + console.log('reconnecting'); + connnect(); + }, 1000); + }); } }, 1000, From 26a61474b56a2291a825e28261f3b6fbd9033ad8 Mon Sep 17 00:00:00 2001 From: Abdhesh Nayak Date: Thu, 14 Mar 2024 12:02:49 +0530 Subject: [PATCH 4/4] :bug: Fixed issue with websocket reconnect --- lib/client/components/logger/index.tsx | 2 +- lib/client/helpers/socket/context.tsx | 66 ++++++++++++++++---------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/lib/client/components/logger/index.tsx b/lib/client/components/logger/index.tsx index f85dd9a34..bdce1fc41 100644 --- a/lib/client/components/logger/index.tsx +++ b/lib/client/components/logger/index.tsx @@ -747,7 +747,7 @@ const LogComp = ({ {isLoading && } {errors.length ? ( -
{JSON.stringify(errors)}
+
{JSON.stringify(errors, null, 2)}
) : ( logs.length > 0 && ( { useDebounce( () => { if (typeof window !== 'undefined') { - try { - sockPromise.current = new Promise((res, rej) => { - let rejected = false; - try { - // eslint-disable-next-line new-cap - const w = new wsock.w3cwebsocket(`${socketUrl}/ws`, '', '', {}); - - w.onmessage = onMessage; - - w.onopen = () => { - res(w); - }; - - w.onerror = (e) => { - console.error('socket closed:', e); - if (!rejected) { - rejected = true; + const connnect = (recon = () => {}) => { + try { + sockPromise.current = new Promise( + (res, rej) => { + try { + // eslint-disable-next-line new-cap + const w = new wsock.w3cwebsocket( + `${socketUrl}/ws`, + '', + '', + {} + ); + + w.onmessage = onMessage; + + w.onopen = () => { + res(w); + }; + + w.onerror = (e) => { + console.error(e); + recon(); + }; + + w.onclose = () => { + recon(); + }; + } catch (e) { rej(e); } - }; + } + ); + } catch (e) { + logger.error(e); + } + }; - w.onclose = () => {}; - } catch (e) { - rej(e); - } - }); - } catch (e) { - logger.error(e); - } + connnect(() => { + setTimeout(() => { + console.log('reconnecting'); + connnect(); + }, 1000); + }); } }, 1000,