diff --git a/apps/web-tss/src/api/createAxiosInstance.test.ts b/apps/web-tss/src/api/createAxiosInstance.test.ts deleted file mode 100644 index 524e5738a..000000000 --- a/apps/web-tss/src/api/createAxiosInstance.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -import axios, { type AxiosError, AxiosHeaders, type InternalAxiosRequestConfig } from 'axios'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { createAxiosInstance, defaultAxiosRequestHandler } from './createAxiosInstance'; -import { rustErrorHandler } from './errors'; - -// Mock dependencies -vi.mock('axios'); -vi.mock('./buster_rest/errors'); -vi.mock('./createServerInstance'); -vi.mock('@tanstack/react-query', () => ({ - isServer: false, -})); - -describe('createAxiosInstance', () => { - const mockBaseURL = 'https://api.example.com'; - - beforeEach(() => { - vi.clearAllMocks(); - (axios.create as any).mockReturnValue({ - interceptors: { - response: { use: vi.fn() }, - request: { use: vi.fn() }, - }, - }); - }); - - it('creates an axios instance with correct configuration', () => { - createAxiosInstance(mockBaseURL); - - expect(axios.create).toHaveBeenCalledWith({ - baseURL: mockBaseURL, - timeout: 120000, - headers: { - 'Content-Type': 'application/json', - }, - }); - }); - - it('sets up response interceptors', () => { - const mockInstance = { - interceptors: { - response: { use: vi.fn() }, - request: { use: vi.fn() }, - }, - }; - (axios.create as any).mockReturnValue(mockInstance); - - createAxiosInstance(mockBaseURL); - - expect(mockInstance.interceptors.response.use).toHaveBeenCalled(); - expect(mockInstance.interceptors.request.use).toHaveBeenCalledWith(defaultAxiosRequestHandler); - }); - - it('handles errors in response interceptor', async () => { - const mockError = new Error('API Error') as AxiosError; - const mockInstance = { - interceptors: { - response: { use: vi.fn() }, - request: { use: vi.fn() }, - }, - }; - (axios.create as any).mockReturnValue(mockInstance); - (rustErrorHandler as any).mockReturnValue('Processed Error'); - - // Get the error handler by capturing the second argument passed to use() - createAxiosInstance(mockBaseURL); - const errorHandler = mockInstance.interceptors.response.use.mock.calls[0][1]; - - // Test the error handler - await expect(errorHandler(mockError)).rejects.toBe('Processed Error'); - expect(rustErrorHandler).toHaveBeenCalledWith(mockError); - }); -}); - -describe('defaultAxiosRequestHandler', () => { - const mockConfig: InternalAxiosRequestConfig = { - headers: new AxiosHeaders(), - method: 'get', - url: 'test', - }; - - beforeEach(() => { - vi.clearAllMocks(); - vi.resetModules(); - }); - - it('adds authorization header with token in client environment', async () => { - const mockToken = 'test-token'; - const mockCheckTokenValidity = vi.fn().mockResolvedValue({ - access_token: mockToken, - isTokenValid: true, - }); - - const result = await defaultAxiosRequestHandler(mockConfig, { - checkTokenValidity: () => Promise.resolve(mockCheckTokenValidity()), - }); - - expect(result.headers.Authorization).toBe(`Bearer ${mockToken}`); - }); - - it('throws error when token is empty', async () => { - const mockCheckTokenValidity = vi.fn().mockResolvedValue({ - access_token: '', - isTokenValid: false, - }); - - // The function should throw an error when no token is available - await expect( - defaultAxiosRequestHandler(mockConfig, { - checkTokenValidity: () => Promise.resolve(mockCheckTokenValidity()), - }) - ).rejects.toThrow('User authentication error - failed to get valid token'); - }); - - it('throws error when checkTokenValidity fails', async () => { - const mockCheckTokenValidity = vi.fn().mockRejectedValue(new Error('Token validation failed')); - - // The function should throw an error when token validation fails - await expect( - defaultAxiosRequestHandler(mockConfig, { - checkTokenValidity: () => Promise.reject(mockCheckTokenValidity()), - }) - ).rejects.toThrow('User authentication error - failed to get valid token'); - }); - - it('preserves existing config properties', async () => { - const originalConfig: InternalAxiosRequestConfig = { - ...mockConfig, - timeout: 5000, - baseURL: 'https://api.example.com', - }; - - const result = await defaultAxiosRequestHandler(originalConfig, { - checkTokenValidity: () => - Promise.resolve({ - access_token: 'token', - isTokenValid: true, - }), - }); - - expect(result.timeout).toBe(5000); - expect(result.baseURL).toBe('https://api.example.com'); - }); -}); diff --git a/apps/web-tss/src/components/ui/icons/customIcons/supabase.tsx b/apps/web-tss/src/components/ui/icons/customIcons/supabase.tsx index 9a7f9e3b8..151cd4ee6 100644 --- a/apps/web-tss/src/components/ui/icons/customIcons/supabase.tsx +++ b/apps/web-tss/src/components/ui/icons/customIcons/supabase.tsx @@ -1,4 +1,4 @@ -import type React from 'react'; +import React from 'react'; export const SupabaseIcon: React.FC<{ onClick?: () => void; @@ -6,6 +6,9 @@ export const SupabaseIcon: React.FC<{ size?: number; className?: string; }> = (props) => { + const uniqueId = React.useId(); + const gradientId = `paint0_linear-${uniqueId}`; + return ( Supabase > = ({ children, user, + accessToken, }) => { return ( - + {children} {/* diff --git a/apps/web-tss/src/context/Supabase/SupabaseContextProvider.tsx b/apps/web-tss/src/context/Supabase/SupabaseContextProvider.tsx index 6844dc961..0c101921f 100644 --- a/apps/web-tss/src/context/Supabase/SupabaseContextProvider.tsx +++ b/apps/web-tss/src/context/Supabase/SupabaseContextProvider.tsx @@ -9,14 +9,19 @@ export type SupabaseContextType = { id: string; is_anonymous: boolean; }; + accessToken: string; }; const supabase = getBrowserClient(); const fiveMinutes = 5 * 60 * 1000; -const useSupabaseContextInternal = ({ user }: SupabaseContextType) => { +const useSupabaseContextInternal = ({ + user, + accessToken: accessTokenProp, +}: SupabaseContextType) => { const refreshTimerRef = useRef | undefined>(undefined); const [supabaseUser, setSupabaseUser] = useState | null>(user); + const [accessToken, setAccessToken] = useState(accessTokenProp); const isAnonymousUser: boolean = !user?.id || user?.is_anonymous === true; @@ -27,9 +32,12 @@ const useSupabaseContextInternal = ({ user }: SupabaseContextType) => { const user = session?.user ?? null; const expiresAt = session?.expires_at ?? 0; const timerMs = expiresAt - fiveMinutes; + const accessToken = session?.access_token ?? ''; setSupabaseUser(user); + if (accessToken) setAccessToken(accessToken); + if (refreshTimerRef.current) { clearTimeout(refreshTimerRef.current); } @@ -48,7 +56,9 @@ const useSupabaseContextInternal = ({ user }: SupabaseContextType) => { }); return { + supabaseUser, isAnonymousUser, + accessToken, }; }; @@ -58,8 +68,8 @@ const SupabaseContext = createContext = React.memo(({ user, children }) => { - const value = useSupabaseContextInternal({ user }); +> = React.memo(({ user, accessToken, children }) => { + const value = useSupabaseContextInternal({ user, accessToken }); return {children}; }); diff --git a/apps/web-tss/src/integrations/supabase/getSupabaseUserServer.ts b/apps/web-tss/src/integrations/supabase/getSupabaseUserServer.ts index 75af3bad6..92a5b6746 100644 --- a/apps/web-tss/src/integrations/supabase/getSupabaseUserServer.ts +++ b/apps/web-tss/src/integrations/supabase/getSupabaseUserServer.ts @@ -23,7 +23,7 @@ function transformToAuthUserDTO(user: User): AuthUserDTO { } export const getSupabaseUser = createServerFn({ method: 'GET' }).handler(async () => { - const supabase = await getSupabaseServerClient(); + const supabase = getSupabaseServerClient(); const { data: userData } = await supabase.auth.getUser(); if (!userData.user) { @@ -43,7 +43,7 @@ export const getSupabaseUser = createServerFn({ method: 'GET' }).handler(async ( created_at: anon.data.user?.created_at ?? '', } satisfies AuthUserDTO, accessToken: anon.data.accessToken, - } as { user: AuthUserDTO; accessToken: string | undefined }; + } as { user: AuthUserDTO; accessToken: string }; } // Get the session first @@ -56,5 +56,5 @@ export const getSupabaseUser = createServerFn({ method: 'GET' }).handler(async ( return { user, accessToken, - } as { user: AuthUserDTO; accessToken: string | undefined }; + } as { user: AuthUserDTO; accessToken: string }; }); diff --git a/apps/web-tss/src/routes/__root.tsx b/apps/web-tss/src/routes/__root.tsx index 096831817..c29c76720 100644 --- a/apps/web-tss/src/routes/__root.tsx +++ b/apps/web-tss/src/routes/__root.tsx @@ -33,7 +33,7 @@ export const Route = createRootRouteWithContext()({ }); function RootDocument({ children }: { children: React.ReactNode }) { - const { user } = Route.useRouteContext(); + const { user, accessToken } = Route.useRouteContext(); return ( @@ -41,7 +41,9 @@ function RootDocument({ children }: { children: React.ReactNode }) { - {children} + + {children} +