mirror of https://github.com/buster-so/buster.git
Create healthcheck.tsx
This commit is contained in:
parent
9f17e2a140
commit
667cbfab7d
|
@ -8,7 +8,7 @@
|
|||
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build -- --typecheck",
|
||||
"build:staging": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode staging",
|
||||
"build:production": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode production -- --typecheck",
|
||||
"build:local": "cross-env NODE_OPTIONS=--max-old-space-size=12288 vite build -- --typecheck --local",
|
||||
"build:local": "pnpm prebuild && cross-env NODE_OPTIONS=--max-old-space-size=12288 vite build -- --typecheck --local",
|
||||
"build-storybook": "storybook build",
|
||||
"build:visualize": "npx vite-bundle-visualizer",
|
||||
"deploy:dev": "pnpm run build && npx wrangler deploy .output/server/index.mjs --env dev --assets .output/public",
|
||||
|
|
|
@ -69,7 +69,6 @@ export const defaultAxiosRequestHandler = async (config: InternalAxiosRequestCon
|
|||
token = await getSupabaseSessionServerFn().then(
|
||||
({ data: { session } }) => session.access_token
|
||||
);
|
||||
console.log('token', token);
|
||||
} else {
|
||||
// Always check token validity before making requests
|
||||
const tokenResult = await checkTokenValidity();
|
||||
|
|
|
@ -11,7 +11,6 @@ export const getSupabaseSessionServerFn = createServerFn({ method: 'GET' }).hand
|
|||
expires_at: session?.expires_at,
|
||||
expires_in: session?.expires_in,
|
||||
};
|
||||
console.log('pickedSession', JSON.stringify(pickedSession));
|
||||
|
||||
return {
|
||||
data: {
|
||||
|
|
|
@ -4,8 +4,6 @@ import { useBusterNotifications } from '@/context/BusterNotifications';
|
|||
import { signOut } from '@/integrations/supabase/signOut';
|
||||
import { clearAllBrowserStorage } from '@/lib/storage';
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
export const useSignOut = () => {
|
||||
const { openErrorMessage } = useBusterNotifications();
|
||||
const navigate = useNavigate();
|
||||
|
@ -14,19 +12,17 @@ export const useSignOut = () => {
|
|||
// Then perform server-side sign out
|
||||
await signOut();
|
||||
|
||||
// First clear all client-side storage
|
||||
clearAllBrowserStorage();
|
||||
|
||||
navigate({ to: '/auth/login' });
|
||||
|
||||
// Clear all cookies
|
||||
for (const cookie of document.cookie.split(';')) {
|
||||
const cookieName = cookie.replace(/^ +/, '').split('=')[0];
|
||||
// biome-ignore lint/suspicious/noDocumentCookie: clearing cookies
|
||||
document.cookie = `${cookieName}=;expires=${new Date().toUTCString()};path=/`;
|
||||
try {
|
||||
// First clear all client-side storage
|
||||
clearAllBrowserStorage();
|
||||
} catch (error) {
|
||||
console.error('Error clearing browser storage', error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error signing out', error);
|
||||
openErrorMessage('Error signing out');
|
||||
} finally {
|
||||
navigate({ to: '/auth/login' });
|
||||
}
|
||||
}, [navigate, openErrorMessage]);
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { rustErrorHandler } from '@/api/errors';
|
||||
import { ErrorCard } from './GlobalErrorCard';
|
||||
|
||||
export const ComponentErrorCard = ({
|
||||
children,
|
||||
header,
|
||||
message,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
header?: string;
|
||||
message?: string;
|
||||
}) => {
|
||||
return (
|
||||
<ErrorBoundary
|
||||
fallbackRender={(e) => {
|
||||
const errorMessage: string | undefined = rustErrorHandler(e).message || undefined;
|
||||
return <ErrorCard header={header} message={errorMessage || message} />;
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
|
@ -3,8 +3,19 @@ import { usePostHog } from 'posthog-js/react';
|
|||
import { useEffect } from 'react';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { Card, CardContent, CardFooter } from '@/components/ui/card/CardBase';
|
||||
import { useMount } from '../../../hooks/useMount';
|
||||
|
||||
export const ErrorCard = ({
|
||||
header = 'Looks like we hit an unexpected error',
|
||||
message = "Our team has been notified via Slack. We'll take a look at the issue ASAP and get back to you.",
|
||||
}: {
|
||||
header?: string;
|
||||
message?: string;
|
||||
}) => {
|
||||
useMount(() => {
|
||||
console.error('Error in card:', header, message);
|
||||
});
|
||||
|
||||
export const ErrorCard = () => {
|
||||
return (
|
||||
<div
|
||||
className=" flex h-full w-full flex-col items-center absolute inset-0 justify-center bg-linear-to-br bg-background p-8 backdrop-blur-xs backdrop-filter"
|
||||
|
@ -13,11 +24,9 @@ export const ErrorCard = () => {
|
|||
<Card className="-mt-10 max-w-100">
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h1 className="text-2xl font-medium">Looks like we hit an unexpected error</h1>
|
||||
<h1 className="text-2xl font-medium">{header}</h1>
|
||||
|
||||
<h5 className="m-0 text-base font-medium text-gray-600">
|
||||
{`Our team has been notified via Slack. We'll take a look at the issue ASAP and get back to you.`}
|
||||
</h5>
|
||||
<h5 className="m-0 text-base font-medium text-gray-600">{message}</h5>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
|
@ -42,8 +51,6 @@ export const GlobalErrorCard: ErrorRouteComponent = ({ error }) => {
|
|||
if (isPosthogLoaded) {
|
||||
posthog.captureException(error);
|
||||
}
|
||||
|
||||
console.error('GlobalErrorCard error', error);
|
||||
}, [error]);
|
||||
|
||||
return <ErrorCard />;
|
||||
|
|
|
@ -6,6 +6,7 @@ import CircleUser from '@/components/ui/icons/NucleoIconOutlined/circle-user';
|
|||
import { createSidebarGroup } from '@/components/ui/sidebar/create-sidebar-item';
|
||||
import LockCircle from '../../ui/icons/NucleoIconOutlined/lock-circle';
|
||||
import { type ISidebarGroup, Sidebar } from '../../ui/sidebar';
|
||||
import { ComponentErrorCard } from '../global/ComponentErrorCard';
|
||||
import { SidebarUserFooter } from './SidebarUserFooter';
|
||||
|
||||
const accountItems: ISidebarGroup = createSidebarGroup({
|
||||
|
@ -107,12 +108,14 @@ export const SidebarSettings = () => {
|
|||
}, [isAdmin]);
|
||||
|
||||
return (
|
||||
<Sidebar
|
||||
content={sidebarItems}
|
||||
header={useMemo(() => <SidebarSettingsHeader />, [])}
|
||||
footer={useMemo(() => <SidebarUserFooter />, [])}
|
||||
useCollapsible={isUserRegistered}
|
||||
/>
|
||||
<ComponentErrorCard header="Settings Sidebar">
|
||||
<Sidebar
|
||||
content={sidebarItems}
|
||||
header={useMemo(() => <SidebarSettingsHeader />, [])}
|
||||
footer={useMemo(() => <SidebarUserFooter />, [])}
|
||||
useCollapsible={isUserRegistered}
|
||||
/>
|
||||
</ComponentErrorCard>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -210,7 +210,6 @@ const InlineComboboxInput = React.forwardRef<
|
|||
})?.width + 8
|
||||
);
|
||||
}, [placeholder]);
|
||||
console.log(placeHolderWidth);
|
||||
|
||||
/**
|
||||
* To create an auto-resizing input, we render a visually hidden span
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
useGetUserBasicInfo,
|
||||
useGetUserOrganization,
|
||||
} from '@/api/buster_rest/users/useGetUserInfo';
|
||||
import { ComponentErrorCard } from '@/components/features/global/ComponentErrorCard';
|
||||
import { isDev } from '@/config/dev';
|
||||
import { env } from '@/env';
|
||||
import packageJson from '../../../package.json';
|
||||
|
@ -16,16 +17,18 @@ const POSTHOG_KEY = env.VITE_PUBLIC_POSTHOG_KEY;
|
|||
const DEBUG_POSTHOG = false;
|
||||
|
||||
export const BusterPosthogProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
console.log('POSTHOG_KEY', POSTHOG_KEY);
|
||||
console.log('DEBUG_POSTHOG', DEBUG_POSTHOG);
|
||||
if ((isDev && !DEBUG_POSTHOG) || !POSTHOG_KEY) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
console.log('POSTHOG_KEY22', POSTHOG_KEY);
|
||||
console.log('DEBUG_POSTHOG22', DEBUG_POSTHOG);
|
||||
|
||||
return <PosthogWrapper>{children}</PosthogWrapper>;
|
||||
return (
|
||||
<ComponentErrorCard
|
||||
header="Posthog failed to load"
|
||||
message="Our team has been notified via Slack. We'll take a look at the issue ASAP and get back to you."
|
||||
>
|
||||
<PosthogWrapper>{children}</PosthogWrapper>
|
||||
</ComponentErrorCard>
|
||||
);
|
||||
};
|
||||
BusterPosthogProvider.displayName = 'BusterPosthogProvider';
|
||||
|
||||
|
@ -49,7 +52,6 @@ const options: Partial<PostHogConfig> = {
|
|||
};
|
||||
|
||||
const PosthogWrapper: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
console.log('PosthogWrapper');
|
||||
const user = useGetUserBasicInfo();
|
||||
const { data: userTeams } = useGetUserTeams({ userId: user?.id ?? '' });
|
||||
const userOrganizations = useGetUserOrganization();
|
||||
|
@ -113,8 +115,6 @@ const PosthogWrapper: React.FC<PropsWithChildren> = ({ children }) => {
|
|||
return <>{children}</>;
|
||||
}
|
||||
|
||||
console.log('PostHogProvider!!!!', PostHogProvider);
|
||||
|
||||
return (
|
||||
<ClientOnly>
|
||||
<PostHogProvider client={posthogModules.posthog}>{children}</PostHogProvider>
|
||||
|
|
|
@ -17,8 +17,6 @@ export const AppProviders: React.FC<PropsWithChildren<SupabaseContextType>> = ({
|
|||
user,
|
||||
accessToken,
|
||||
}) => {
|
||||
console.log('user11', user);
|
||||
console.log('accessToken11', accessToken);
|
||||
return (
|
||||
<SupabaseContextProvider user={user} accessToken={accessToken}>
|
||||
<BusterPosthogProvider>{children}</BusterPosthogProvider>
|
||||
|
|
|
@ -66,10 +66,7 @@ export const SupabaseContext = createContext<ReturnType<typeof useSupabaseContex
|
|||
export const SupabaseContextProvider: React.FC<
|
||||
SupabaseContextType & { children: React.ReactNode }
|
||||
> = React.memo(({ user, accessToken, children }) => {
|
||||
console.log('user22', user);
|
||||
console.log('accessToken22', accessToken);
|
||||
const value = useSupabaseContextInternal({ user, accessToken });
|
||||
console.log('value22', value);
|
||||
|
||||
return <SupabaseContext.Provider value={value}>{children}</SupabaseContext.Provider>;
|
||||
});
|
||||
|
|
|
@ -13,8 +13,6 @@ const isValidRedirectUrl = (url: string): boolean => {
|
|||
}
|
||||
};
|
||||
|
||||
const fallbackCallbackUrl = `${env.VITE_PUBLIC_URL}/auth/callback`;
|
||||
|
||||
// Common OAuth handler to reduce code duplication
|
||||
const handleOAuthSignIn = async (
|
||||
provider: 'google' | 'github' | 'azure',
|
||||
|
@ -55,6 +53,7 @@ export const signInWithEmailAndPassword = createServerFn({ method: 'POST' })
|
|||
)
|
||||
.handler(async ({ data }) => {
|
||||
const supabase = getSupabaseServerClient();
|
||||
|
||||
const { error } = await supabase.auth.signInWithPassword({
|
||||
email: data.email,
|
||||
password: data.password,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type React from 'react';
|
||||
import { ComponentErrorCard } from '@/components/features/global/ComponentErrorCard';
|
||||
import { SidebarSettings } from '@/components/features/sidebars/SidebarSettings';
|
||||
import { AppLayout, type LayoutSize } from '@/components/ui/layouts/AppLayout';
|
||||
|
||||
|
@ -16,13 +17,15 @@ export const SettingsAppLayout: React.FC<IPrimaryAppLayoutProps> = ({
|
|||
defaultLayout,
|
||||
}) => {
|
||||
return (
|
||||
<AppLayout
|
||||
autoSaveId={layoutId}
|
||||
defaultLayout={defaultLayout}
|
||||
initialLayout={initialLayout}
|
||||
sidebar={<SidebarSettings />}
|
||||
>
|
||||
{children}
|
||||
</AppLayout>
|
||||
<ComponentErrorCard header="Settings App Layout">
|
||||
<AppLayout
|
||||
autoSaveId={layoutId}
|
||||
defaultLayout={defaultLayout}
|
||||
initialLayout={initialLayout}
|
||||
sidebar={<SidebarSettings />}
|
||||
>
|
||||
{children}
|
||||
</AppLayout>
|
||||
</ComponentErrorCard>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -20,6 +20,7 @@ import { Route as AppIndexRouteImport } from './routes/app/index'
|
|||
import { Route as AuthResetPasswordRouteImport } from './routes/auth.reset-password'
|
||||
import { Route as AuthLogoutRouteImport } from './routes/auth.logout'
|
||||
import { Route as AuthLoginRouteImport } from './routes/auth.login'
|
||||
import { Route as AppHealthcheckRouteImport } from './routes/app/healthcheck'
|
||||
import { Route as AppSettingsRouteImport } from './routes/app/_settings'
|
||||
import { Route as AppAppRouteImport } from './routes/app/_app'
|
||||
import { Route as EmbedReportReportIdRouteImport } from './routes/embed.report.$reportId'
|
||||
|
@ -29,6 +30,7 @@ import { Route as AppSettingsRestricted_layoutRouteImport } from './routes/app/_
|
|||
import { Route as AppSettingsPermissionsRouteImport } from './routes/app/_settings/_permissions'
|
||||
import { Route as AppAppHomeRouteImport } from './routes/app/_app/home'
|
||||
import { Route as AppAppAssetRouteImport } from './routes/app/_app/_asset'
|
||||
import { Route as AppSettingsSettingsIndexRouteImport } from './routes/app/_settings/settings.index'
|
||||
import { Route as AppAppReportsIndexRouteImport } from './routes/app/_app/reports.index'
|
||||
import { Route as AppAppMetricsIndexRouteImport } from './routes/app/_app/metrics.index'
|
||||
import { Route as AppAppLogsIndexRouteImport } from './routes/app/_app/logs.index'
|
||||
|
@ -198,6 +200,11 @@ const AuthLoginRoute = AuthLoginRouteImport.update({
|
|||
path: '/login',
|
||||
getParentRoute: () => AuthRoute,
|
||||
} as any)
|
||||
const AppHealthcheckRoute = AppHealthcheckRouteImport.update({
|
||||
id: '/healthcheck',
|
||||
path: '/healthcheck',
|
||||
getParentRoute: () => AppRoute,
|
||||
} as any)
|
||||
const AppSettingsRoute = AppSettingsRouteImport.update({
|
||||
id: '/_settings',
|
||||
getParentRoute: () => AppRoute,
|
||||
|
@ -240,6 +247,12 @@ const AppAppAssetRoute = AppAppAssetRouteImport.update({
|
|||
id: '/_asset',
|
||||
getParentRoute: () => AppAppRoute,
|
||||
} as any)
|
||||
const AppSettingsSettingsIndexRoute =
|
||||
AppSettingsSettingsIndexRouteImport.update({
|
||||
id: '/settings/',
|
||||
path: '/settings/',
|
||||
getParentRoute: () => AppSettingsRoute,
|
||||
} as any)
|
||||
const AppAppReportsIndexRoute = AppAppReportsIndexRouteImport.update({
|
||||
id: '/reports/',
|
||||
path: '/reports/',
|
||||
|
@ -913,6 +926,7 @@ export interface FileRoutesByFullPath {
|
|||
'/app': typeof AppSettingsRestricted_layoutAdmin_onlyRouteWithChildren
|
||||
'/auth': typeof AuthRouteWithChildren
|
||||
'/healthcheck': typeof HealthcheckRoute
|
||||
'/app/healthcheck': typeof AppHealthcheckRoute
|
||||
'/auth/login': typeof AuthLoginRoute
|
||||
'/auth/logout': typeof AuthLogoutRoute
|
||||
'/auth/reset-password': typeof AuthResetPasswordRoute
|
||||
|
@ -929,6 +943,7 @@ export interface FileRoutesByFullPath {
|
|||
'/app/logs': typeof AppAppLogsIndexRoute
|
||||
'/app/metrics': typeof AppAppMetricsIndexRoute
|
||||
'/app/reports': typeof AppAppReportsIndexRoute
|
||||
'/app/settings': typeof AppSettingsSettingsIndexRoute
|
||||
'/app/chats/$chatId': typeof AppAppAssetChatsChatIdRouteWithChildren
|
||||
'/app/datasets/$datasetId/editor': typeof AppAppDatasetsDatasetIdEditorRoute
|
||||
'/app/datasets/$datasetId/overview': typeof AppAppDatasetsDatasetIdOverviewRoute
|
||||
|
@ -1018,6 +1033,7 @@ export interface FileRoutesByTo {
|
|||
'/auth': typeof AuthRouteWithChildren
|
||||
'/healthcheck': typeof HealthcheckRoute
|
||||
'/app': typeof AppSettingsRestricted_layoutAdmin_onlyRouteWithChildren
|
||||
'/app/healthcheck': typeof AppHealthcheckRoute
|
||||
'/auth/login': typeof AuthLoginRoute
|
||||
'/auth/logout': typeof AuthLogoutRoute
|
||||
'/auth/reset-password': typeof AuthResetPasswordRoute
|
||||
|
@ -1033,6 +1049,7 @@ export interface FileRoutesByTo {
|
|||
'/app/logs': typeof AppAppLogsIndexRoute
|
||||
'/app/metrics': typeof AppAppMetricsIndexRoute
|
||||
'/app/reports': typeof AppAppReportsIndexRoute
|
||||
'/app/settings': typeof AppSettingsSettingsIndexRoute
|
||||
'/app/datasets/$datasetId/editor': typeof AppAppDatasetsDatasetIdEditorRoute
|
||||
'/app/datasets/$datasetId/overview': typeof AppAppDatasetsDatasetIdOverviewRoute
|
||||
'/app/settings/profile': typeof AppSettingsRestricted_layoutSettingsProfileRoute
|
||||
|
@ -1108,6 +1125,7 @@ export interface FileRoutesById {
|
|||
'/healthcheck': typeof HealthcheckRoute
|
||||
'/app/_app': typeof AppAppRouteWithChildren
|
||||
'/app/_settings': typeof AppSettingsRouteWithChildren
|
||||
'/app/healthcheck': typeof AppHealthcheckRoute
|
||||
'/auth/login': typeof AuthLoginRoute
|
||||
'/auth/logout': typeof AuthLogoutRoute
|
||||
'/auth/reset-password': typeof AuthResetPasswordRoute
|
||||
|
@ -1128,6 +1146,7 @@ export interface FileRoutesById {
|
|||
'/app/_app/logs/': typeof AppAppLogsIndexRoute
|
||||
'/app/_app/metrics/': typeof AppAppMetricsIndexRoute
|
||||
'/app/_app/reports/': typeof AppAppReportsIndexRoute
|
||||
'/app/_settings/settings/': typeof AppSettingsSettingsIndexRoute
|
||||
'/app/_app/_asset/chats/$chatId': typeof AppAppAssetChatsChatIdRouteWithChildren
|
||||
'/app/_app/datasets/$datasetId/editor': typeof AppAppDatasetsDatasetIdEditorRoute
|
||||
'/app/_app/datasets/$datasetId/overview': typeof AppAppDatasetsDatasetIdOverviewRoute
|
||||
|
@ -1229,6 +1248,7 @@ export interface FileRouteTypes {
|
|||
| '/app'
|
||||
| '/auth'
|
||||
| '/healthcheck'
|
||||
| '/app/healthcheck'
|
||||
| '/auth/login'
|
||||
| '/auth/logout'
|
||||
| '/auth/reset-password'
|
||||
|
@ -1245,6 +1265,7 @@ export interface FileRouteTypes {
|
|||
| '/app/logs'
|
||||
| '/app/metrics'
|
||||
| '/app/reports'
|
||||
| '/app/settings'
|
||||
| '/app/chats/$chatId'
|
||||
| '/app/datasets/$datasetId/editor'
|
||||
| '/app/datasets/$datasetId/overview'
|
||||
|
@ -1334,6 +1355,7 @@ export interface FileRouteTypes {
|
|||
| '/auth'
|
||||
| '/healthcheck'
|
||||
| '/app'
|
||||
| '/app/healthcheck'
|
||||
| '/auth/login'
|
||||
| '/auth/logout'
|
||||
| '/auth/reset-password'
|
||||
|
@ -1349,6 +1371,7 @@ export interface FileRouteTypes {
|
|||
| '/app/logs'
|
||||
| '/app/metrics'
|
||||
| '/app/reports'
|
||||
| '/app/settings'
|
||||
| '/app/datasets/$datasetId/editor'
|
||||
| '/app/datasets/$datasetId/overview'
|
||||
| '/app/settings/profile'
|
||||
|
@ -1423,6 +1446,7 @@ export interface FileRouteTypes {
|
|||
| '/healthcheck'
|
||||
| '/app/_app'
|
||||
| '/app/_settings'
|
||||
| '/app/healthcheck'
|
||||
| '/auth/login'
|
||||
| '/auth/logout'
|
||||
| '/auth/reset-password'
|
||||
|
@ -1443,6 +1467,7 @@ export interface FileRouteTypes {
|
|||
| '/app/_app/logs/'
|
||||
| '/app/_app/metrics/'
|
||||
| '/app/_app/reports/'
|
||||
| '/app/_settings/settings/'
|
||||
| '/app/_app/_asset/chats/$chatId'
|
||||
| '/app/_app/datasets/$datasetId/editor'
|
||||
| '/app/_app/datasets/$datasetId/overview'
|
||||
|
@ -1627,6 +1652,13 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof AuthLoginRouteImport
|
||||
parentRoute: typeof AuthRoute
|
||||
}
|
||||
'/app/healthcheck': {
|
||||
id: '/app/healthcheck'
|
||||
path: '/healthcheck'
|
||||
fullPath: '/app/healthcheck'
|
||||
preLoaderRoute: typeof AppHealthcheckRouteImport
|
||||
parentRoute: typeof AppRoute
|
||||
}
|
||||
'/app/_settings': {
|
||||
id: '/app/_settings'
|
||||
path: ''
|
||||
|
@ -1690,6 +1722,13 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof AppAppAssetRouteImport
|
||||
parentRoute: typeof AppAppRoute
|
||||
}
|
||||
'/app/_settings/settings/': {
|
||||
id: '/app/_settings/settings/'
|
||||
path: '/settings'
|
||||
fullPath: '/app/settings'
|
||||
preLoaderRoute: typeof AppSettingsSettingsIndexRouteImport
|
||||
parentRoute: typeof AppSettingsRoute
|
||||
}
|
||||
'/app/_app/reports/': {
|
||||
id: '/app/_app/reports/'
|
||||
path: '/reports'
|
||||
|
@ -3120,12 +3159,14 @@ const AppSettingsRestricted_layoutRouteWithChildren =
|
|||
interface AppSettingsRouteChildren {
|
||||
AppSettingsPermissionsRoute: typeof AppSettingsPermissionsRouteWithChildren
|
||||
AppSettingsRestricted_layoutRoute: typeof AppSettingsRestricted_layoutRouteWithChildren
|
||||
AppSettingsSettingsIndexRoute: typeof AppSettingsSettingsIndexRoute
|
||||
}
|
||||
|
||||
const AppSettingsRouteChildren: AppSettingsRouteChildren = {
|
||||
AppSettingsPermissionsRoute: AppSettingsPermissionsRouteWithChildren,
|
||||
AppSettingsRestricted_layoutRoute:
|
||||
AppSettingsRestricted_layoutRouteWithChildren,
|
||||
AppSettingsSettingsIndexRoute: AppSettingsSettingsIndexRoute,
|
||||
}
|
||||
|
||||
const AppSettingsRouteWithChildren = AppSettingsRoute._addFileChildren(
|
||||
|
@ -3135,12 +3176,14 @@ const AppSettingsRouteWithChildren = AppSettingsRoute._addFileChildren(
|
|||
interface AppRouteChildren {
|
||||
AppAppRoute: typeof AppAppRouteWithChildren
|
||||
AppSettingsRoute: typeof AppSettingsRouteWithChildren
|
||||
AppHealthcheckRoute: typeof AppHealthcheckRoute
|
||||
AppIndexRoute: typeof AppIndexRoute
|
||||
}
|
||||
|
||||
const AppRouteChildren: AppRouteChildren = {
|
||||
AppAppRoute: AppAppRouteWithChildren,
|
||||
AppSettingsRoute: AppSettingsRouteWithChildren,
|
||||
AppHealthcheckRoute: AppHealthcheckRoute,
|
||||
AppIndexRoute: AppIndexRoute,
|
||||
}
|
||||
|
||||
|
|
|
@ -28,22 +28,14 @@ export const Route = createFileRoute('/app')({
|
|||
loader: async ({ context }) => {
|
||||
const { queryClient, accessToken } = context;
|
||||
try {
|
||||
const [initialLayout, user, userInfo, userFavorites, listDatasources, datasets] =
|
||||
await Promise.all([
|
||||
getAppLayout({ id: PRIMARY_APP_LAYOUT_ID }),
|
||||
getSupabaseUser(),
|
||||
prefetchGetMyUserInfo(queryClient),
|
||||
prefetchGetUserFavorites(queryClient),
|
||||
prefetchListDatasources(queryClient),
|
||||
prefetchGetDatasets(queryClient),
|
||||
]);
|
||||
|
||||
console.log('initialLayout', initialLayout);
|
||||
console.log('user', user);
|
||||
console.log('userInfo', userInfo);
|
||||
console.log('userFavorites', userFavorites);
|
||||
console.log('listDatasources', listDatasources);
|
||||
console.log('datasets', datasets);
|
||||
const [initialLayout, user] = await Promise.all([
|
||||
getAppLayout({ id: PRIMARY_APP_LAYOUT_ID }),
|
||||
getSupabaseUser(),
|
||||
prefetchGetMyUserInfo(queryClient),
|
||||
prefetchGetUserFavorites(queryClient),
|
||||
prefetchListDatasources(queryClient),
|
||||
prefetchGetDatasets(queryClient),
|
||||
]);
|
||||
|
||||
if (!user) {
|
||||
throw redirect({ to: '/auth/login' });
|
||||
|
@ -63,8 +55,6 @@ export const Route = createFileRoute('/app')({
|
|||
},
|
||||
component: () => {
|
||||
const { user, accessToken } = Route.useLoaderData();
|
||||
console.log('user', user);
|
||||
console.log('accessToken', accessToken);
|
||||
|
||||
return (
|
||||
<AppProviders user={user} accessToken={accessToken}>
|
||||
|
|
|
@ -6,6 +6,7 @@ const routeApi = getRouteApi('/app');
|
|||
export const Route = createFileRoute('/app/_settings')({
|
||||
component: () => {
|
||||
const { initialLayout, layoutId, defaultLayout } = routeApi.useLoaderData();
|
||||
|
||||
return (
|
||||
<SettingsAppLayout
|
||||
initialLayout={initialLayout}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/app/_settings/settings/')({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/app/_settings/"!</div>;
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
import type { HealthCheckResponse } from '@buster/server-shared/healthcheck';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { useHealthcheck } from '@/api/buster_rest/healthcheck/queryRequests';
|
||||
import { useGetUserBasicInfo } from '@/api/buster_rest/users/useGetUserInfo';
|
||||
import type { RustApiError } from '@/api/errors';
|
||||
import { useGetSupabaseUser } from '@/context/Supabase';
|
||||
|
||||
export const Route = createFileRoute('/app/healthcheck')({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { data, isLoading, error } = useHealthcheck();
|
||||
const supabaseUser = useGetSupabaseUser();
|
||||
const user = useGetUserBasicInfo();
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingState />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorState error={error} />;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return <ErrorState error={new Error('No data received')} />;
|
||||
}
|
||||
|
||||
return <HealthcheckDashboard data={data} supabaseUser={supabaseUser} user={user} />;
|
||||
}
|
||||
|
||||
function LoadingState() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-white flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||
<p className="text-gray-600">Checking system health...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ErrorState({ error }: { error: Error | RustApiError }) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-red-50 to-white flex items-center justify-center">
|
||||
<div className="bg-white rounded-lg shadow-lg p-8 max-w-md w-full mx-4">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center mr-4">
|
||||
<svg
|
||||
className="w-6 h-6 text-red-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-gray-900">Health Check Failed</h2>
|
||||
<p className="text-gray-600">Unable to retrieve system status</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-red-50 border border-red-200 rounded-md p-3">
|
||||
<p className="text-sm text-red-800">{error.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HealthcheckDashboard({
|
||||
data,
|
||||
supabaseUser,
|
||||
user,
|
||||
}: {
|
||||
data: HealthCheckResponse;
|
||||
supabaseUser: ReturnType<typeof useGetSupabaseUser>;
|
||||
user: ReturnType<typeof useGetUserBasicInfo>;
|
||||
}) {
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'healthy':
|
||||
case 'pass':
|
||||
return 'text-green-600 bg-green-100';
|
||||
case 'degraded':
|
||||
case 'warn':
|
||||
return 'text-yellow-600 bg-yellow-100';
|
||||
case 'unhealthy':
|
||||
case 'fail':
|
||||
return 'text-red-600 bg-red-100';
|
||||
default:
|
||||
return 'text-gray-600 bg-gray-100';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusIcon = (status: string) => {
|
||||
switch (status) {
|
||||
case 'healthy':
|
||||
case 'pass':
|
||||
return (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
);
|
||||
case 'degraded':
|
||||
case 'warn':
|
||||
return (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case 'unhealthy':
|
||||
case 'fail':
|
||||
return (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const formatUptime = (seconds: number) => {
|
||||
const days = Math.floor(seconds / 86400);
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
|
||||
if (days > 0) {
|
||||
return `${days}d ${hours}h ${minutes}m`;
|
||||
}
|
||||
if (hours > 0) {
|
||||
return `${hours}h ${minutes}m`;
|
||||
}
|
||||
return `${minutes}m`;
|
||||
};
|
||||
|
||||
const formatTimestamp = (timestamp: string) => {
|
||||
return new Date(timestamp).toLocaleString();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-white p-6">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">System Health Dashboard</h1>
|
||||
<p className="text-gray-600">Real-time monitoring of system components</p>
|
||||
</div>
|
||||
|
||||
{/* User Data Section */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
|
||||
{/* Supabase User Data */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4 flex items-center">
|
||||
<div className="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center mr-3">
|
||||
<svg
|
||||
className="w-4 h-4 text-blue-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
Supabase User
|
||||
</h3>
|
||||
<div className="bg-gray-50 rounded-md p-4">
|
||||
<pre className="text-sm text-gray-800 whitespace-pre-wrap overflow-auto">
|
||||
{JSON.stringify(supabaseUser, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* User Basic Info */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4 flex items-center">
|
||||
<div className="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center mr-3">
|
||||
<svg
|
||||
className="w-4 h-4 text-green-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
User Basic Info
|
||||
</h3>
|
||||
<div className="bg-gray-50 rounded-md p-4">
|
||||
<pre className="text-sm text-gray-800 whitespace-pre-wrap overflow-auto">
|
||||
{JSON.stringify(user, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Overall Status Card */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className={`w-12 h-12 rounded-full flex items-center justify-center mr-4 ${getStatusColor(
|
||||
data.status
|
||||
)}`}
|
||||
>
|
||||
{getStatusIcon(data.status)}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold text-gray-900 capitalize">{data.status}</h2>
|
||||
<p className="text-gray-600">Overall System Status</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-sm text-gray-500">Last checked</p>
|
||||
<p className="text-gray-900 font-medium">{formatTimestamp(data.timestamp)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metrics Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<div className="flex items-center">
|
||||
<div className="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mr-3">
|
||||
<svg
|
||||
className="w-5 h-5 text-blue-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Uptime</p>
|
||||
<p className="text-xl font-semibold text-gray-900">{formatUptime(data.uptime)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<div className="flex items-center">
|
||||
<div className="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center mr-3">
|
||||
<svg
|
||||
className="w-5 h-5 text-purple-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M7 4V2a1 1 0 011-1h8a1 1 0 011 1v2h4a1 1 0 011 1v1a1 1 0 01-1 1h-1v12a2 2 0 01-2 2H6a2 2 0 01-2-2V7H3a1 1 0 01-1-1V5a1 1 0 011-1h4z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Version</p>
|
||||
<p className="text-xl font-semibold text-gray-900">{data.version}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<div className="flex items-center">
|
||||
<div className="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center mr-3">
|
||||
<svg
|
||||
className="w-5 h-5 text-green-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9v-9m0-9v9"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Environment</p>
|
||||
<p className="text-xl font-semibold text-gray-900 capitalize">{data.environment}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Component Checks */}
|
||||
<div className="bg-white rounded-lg shadow-lg p-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-6">Component Health Checks</h3>
|
||||
<div className="space-y-4">
|
||||
{Object.entries(data.checks).map(([componentName, check]) => (
|
||||
<div key={componentName} className="border border-gray-200 rounded-lg p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className={`w-8 h-8 rounded-full flex items-center justify-center mr-3 ${getStatusColor(
|
||||
check.status
|
||||
)}`}
|
||||
>
|
||||
{getStatusIcon(check.status)}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 capitalize">{componentName}</h4>
|
||||
{check.message && <p className="text-sm text-gray-600">{check.message}</p>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<span
|
||||
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium capitalize ${getStatusColor(
|
||||
check.status
|
||||
)}`}
|
||||
>
|
||||
{check.status}
|
||||
</span>
|
||||
{check.responseTime && (
|
||||
<p className="text-sm text-gray-500 mt-1">{check.responseTime}ms</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-8 text-center text-gray-500 text-sm">
|
||||
<p>System health is monitored continuously. Data refreshes automatically.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -10,7 +10,8 @@ const config = defineConfig(({ command, mode }) => {
|
|||
const isProduction = mode === 'production' || mode === 'staging';
|
||||
const isTypecheck = process.argv.includes('--typecheck') || process.env.TYPECHECK === 'true';
|
||||
const useChecker = !process.env.VITEST && isBuild;
|
||||
const isLocalBuild = process.argv.includes('--local');
|
||||
const isLocalBuild = process.argv.includes('--local') || mode === 'development';
|
||||
const target = isLocalBuild ? ('bun' as const) : ('cloudflare-module' as const);
|
||||
|
||||
return {
|
||||
server: { port: 3000 },
|
||||
|
@ -20,7 +21,7 @@ const config = defineConfig(({ command, mode }) => {
|
|||
tailwindcss(),
|
||||
tanstackStart({
|
||||
customViteReactPlugin: true,
|
||||
target: isLocalBuild ? 'bun' : 'cloudflare-module',
|
||||
target,
|
||||
}),
|
||||
viteReact(),
|
||||
useChecker
|
||||
|
|
Loading…
Reference in New Issue