Refactor auto-generated schemas and types formatting
Updated the formatting for auto-generated schemas, types, and related files to adhere to consistent coding standards (e.g., double quotes, indentation). No functional changes were made, ensuring behavior remains identical.
This commit is contained in:
@@ -1,45 +1,53 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import React, {createContext, useContext, useState, useEffect, ReactNode, useCallback, useMemo} from 'react';
|
||||
import {useRouter, usePathname} from 'next/navigation';
|
||||
import {jwtDecode} from 'jwt-decode';
|
||||
import {client} from '@/client/client.gen';
|
||||
import {useMutation, useQueryClient, useQuery, type UseQueryOptions} from '@tanstack/react-query';
|
||||
import {loginMutation, getCurrentUserInfoOptions} from '@/client/@tanstack/react-query.gen';
|
||||
import {LoginRequest, UserResponse, Token} from '@/client/types.gen';
|
||||
import React, {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { client } from "@/client/client.gen";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
getCurrentUserInfoOptions,
|
||||
loginMutation,
|
||||
} from "@/client/@tanstack/react-query.gen";
|
||||
import { LoginRequest, Token, UserResponse } from "@/client/types.gen";
|
||||
|
||||
// JWT token payload interface
|
||||
interface TokenPayload {
|
||||
sub: string;
|
||||
exp: number;
|
||||
role?: string;
|
||||
sub: string;
|
||||
exp: number;
|
||||
role?: string;
|
||||
|
||||
[key: string]: any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// Auth context state interface
|
||||
interface AuthContextState {
|
||||
user: UserResponse | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
error: Error | null;
|
||||
login: (credentials: LoginRequest) => Promise<void>;
|
||||
logout: () => void;
|
||||
refreshToken: () => Promise<void>;
|
||||
user: UserResponse | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
error: Error | null;
|
||||
login: (credentials: LoginRequest) => Promise<void>;
|
||||
logout: () => void;
|
||||
refreshToken: () => Promise<void>;
|
||||
}
|
||||
|
||||
// Default context state
|
||||
const defaultAuthState: AuthContextState = {
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
isLoading: true,
|
||||
error: null,
|
||||
login: async () => {
|
||||
},
|
||||
logout: () => {
|
||||
},
|
||||
refreshToken: async () => {
|
||||
},
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
isLoading: true,
|
||||
error: null,
|
||||
login: async () => {},
|
||||
logout: () => {},
|
||||
refreshToken: async () => {},
|
||||
};
|
||||
|
||||
// Create context
|
||||
@@ -47,255 +55,256 @@ const AuthContext = createContext<AuthContextState>(defaultAuthState);
|
||||
|
||||
// Custom hook to use the auth context
|
||||
export const useAuth = () => {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuth must be used within an AuthProvider');
|
||||
}
|
||||
return context;
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error("useAuth must be used within an AuthProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// Token management
|
||||
const TOKEN_KEY = 'accessToken';
|
||||
const REFRESH_TOKEN_KEY = 'refreshToken';
|
||||
const TOKEN_KEY = "accessToken";
|
||||
const REFRESH_TOKEN_KEY = "refreshToken";
|
||||
|
||||
// Get token from storage
|
||||
const getStoredToken = (): string | null => {
|
||||
if (typeof window === 'undefined') return null;
|
||||
return localStorage.getItem(TOKEN_KEY);
|
||||
if (typeof window === "undefined") return null;
|
||||
return localStorage.getItem(TOKEN_KEY);
|
||||
};
|
||||
|
||||
// Store token in storage
|
||||
const storeToken = (token: string | null, refreshToken: string | null = null): void => {
|
||||
if (typeof window === 'undefined') return;
|
||||
const storeToken = (
|
||||
token: string | null,
|
||||
refreshToken: string | null = null,
|
||||
): void => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
if (token) {
|
||||
localStorage.setItem(TOKEN_KEY, token);
|
||||
if (refreshToken) {
|
||||
localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken);
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
||||
if (token) {
|
||||
localStorage.setItem(TOKEN_KEY, token);
|
||||
if (refreshToken) {
|
||||
localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken);
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
||||
}
|
||||
};
|
||||
|
||||
// Check if token is expired
|
||||
const isTokenExpired = (token: string): boolean => {
|
||||
try {
|
||||
const decoded = jwtDecode<TokenPayload>(token);
|
||||
const currentTime = Date.now() / 1000;
|
||||
// Add a buffer of 10 seconds to prevent edge cases
|
||||
return decoded.exp < currentTime - 10;
|
||||
} catch (error) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
const decoded = jwtDecode<TokenPayload>(token);
|
||||
const currentTime = Date.now() / 1000;
|
||||
// Add a buffer of 10 seconds to prevent edge cases
|
||||
return decoded.exp < currentTime - 10;
|
||||
} catch (error) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Auth Provider Props
|
||||
interface AuthProviderProps {
|
||||
children: ReactNode;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
// Auth Provider Component
|
||||
export const AuthProvider: React.FC<AuthProviderProps> = ({children}: AuthProviderProps) => {
|
||||
const [isInitializing, setIsInitializing] = useState(true);
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const queryClient = useQueryClient();
|
||||
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
||||
const [isInitializing, setIsInitializing] = useState(true);
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
// Set up login mutation
|
||||
const loginMutationHook = useMutation(loginMutation());
|
||||
// Set up login mutation
|
||||
const loginMutationHook = useMutation(loginMutation());
|
||||
|
||||
// Configure API client with token if available
|
||||
useEffect(() => {
|
||||
const token = getStoredToken();
|
||||
if (token) {
|
||||
client.setConfig({
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
// Configure API client with token if available
|
||||
useEffect(() => {
|
||||
const token = getStoredToken();
|
||||
if (token) {
|
||||
client.setConfig({
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const hasValidToken = useCallback((): boolean => {
|
||||
const token = getStoredToken();
|
||||
return Boolean(token && !isTokenExpired(token));
|
||||
}, []);
|
||||
// Check if current token is valid (not expired)
|
||||
const hasValidToken = useCallback((): boolean => {
|
||||
const token = getStoredToken();
|
||||
return Boolean(token && !isTokenExpired(token));
|
||||
}, []);
|
||||
|
||||
// Create query options
|
||||
const userQueryOptions = {
|
||||
...getCurrentUserInfoOptions(),
|
||||
enabled: hasValidToken(),
|
||||
retry: false,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
} as UseQueryOptions;
|
||||
// Create query options
|
||||
const userQueryOptions = {
|
||||
...getCurrentUserInfoOptions(),
|
||||
enabled: hasValidToken(),
|
||||
retry: false,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
};
|
||||
|
||||
// Use the query without onError in the options
|
||||
const {data: user, isLoading, error, refetch} = useQuery(userQueryOptions);
|
||||
// Use the query without onError in the options
|
||||
const { data: user, isLoading, error, refetch } = useQuery(userQueryOptions);
|
||||
|
||||
// Handle error with a separate effect
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
// Clear tokens on error (unauthorized)
|
||||
storeToken(null);
|
||||
}
|
||||
}, [error]);
|
||||
// Handle error with a separate effect
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
// Clear tokens on error (unauthorized)
|
||||
storeToken(null);
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
// Login function
|
||||
const login = useCallback(async (credentials: LoginRequest): Promise<void> => {
|
||||
try {
|
||||
const response = await loginMutationHook.mutateAsync({
|
||||
body: credentials
|
||||
});
|
||||
// Login function
|
||||
const login = useCallback(
|
||||
async (credentials: LoginRequest): Promise<void> => {
|
||||
try {
|
||||
const response = await loginMutationHook.mutateAsync({
|
||||
body: credentials,
|
||||
});
|
||||
|
||||
// Store tokens
|
||||
storeToken(response.access_token, response.refresh_token || null);
|
||||
// Store tokens
|
||||
storeToken(response.access_token, response.refresh_token || null);
|
||||
|
||||
// Configure client with new token
|
||||
client.setConfig({
|
||||
headers: {
|
||||
Authorization: `Bearer ${response.access_token}`
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger user data fetch
|
||||
await refetch();
|
||||
|
||||
// Redirect after login
|
||||
if (pathname === '/login') {
|
||||
router.push('/dashboard');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}, [loginMutationHook, refetch, router, pathname]);
|
||||
|
||||
// Logout function
|
||||
const logout = useCallback((): void => {
|
||||
// Clear tokens
|
||||
storeToken(null);
|
||||
|
||||
// Remove auth headers
|
||||
// Configure client with new token
|
||||
client.setConfig({
|
||||
headers: {
|
||||
Authorization: undefined
|
||||
}
|
||||
headers: {
|
||||
Authorization: `Bearer ${response.access_token}`,
|
||||
},
|
||||
});
|
||||
|
||||
// Clear user from cache
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getCurrentUserInfoOptions().queryKey
|
||||
// Trigger user data fetch
|
||||
await refetch();
|
||||
|
||||
// Redirect after login
|
||||
if (pathname === "/login") {
|
||||
router.push("/dashboard");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Login failed:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[loginMutationHook, refetch, router, pathname],
|
||||
);
|
||||
|
||||
// Logout function
|
||||
const logout = useCallback((): void => {
|
||||
// Clear tokens
|
||||
storeToken(null);
|
||||
|
||||
// Remove auth headers
|
||||
client.setConfig({
|
||||
headers: {
|
||||
Authorization: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
// Clear user from cache
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getCurrentUserInfoOptions().queryKey,
|
||||
});
|
||||
|
||||
// Redirect to login page
|
||||
router.push("/login");
|
||||
}, [router, queryClient]);
|
||||
|
||||
// Refresh token function
|
||||
const refreshToken = useCallback(async (): Promise<void> => {
|
||||
const refreshTokenValue = localStorage.getItem(REFRESH_TOKEN_KEY);
|
||||
|
||||
if (!refreshTokenValue) {
|
||||
logout();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await client.post<Token>({
|
||||
url: "/api/v1/auth/refresh",
|
||||
body: { refresh_token: refreshTokenValue },
|
||||
});
|
||||
|
||||
const newTokens = response.data;
|
||||
|
||||
if (newTokens) {
|
||||
// Store new tokens
|
||||
storeToken(newTokens.access_token, newTokens.refresh_token || null);
|
||||
|
||||
// Update client headers
|
||||
client.setConfig({
|
||||
headers: {
|
||||
Authorization: `Bearer ${newTokens.access_token}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
// Refetch user data
|
||||
await refetch();
|
||||
} catch (error) {
|
||||
console.error("Token refresh failed:", error);
|
||||
logout();
|
||||
}
|
||||
}, [logout, refetch]);
|
||||
|
||||
// Redirect to login page
|
||||
router.push('/login');
|
||||
}, [router, queryClient]);
|
||||
// Check token validity and refresh if needed on route changes
|
||||
useEffect(() => {
|
||||
const token = getStoredToken();
|
||||
|
||||
// Refresh token function
|
||||
const refreshToken = useCallback(async (): Promise<void> => {
|
||||
const refreshTokenValue = localStorage.getItem(REFRESH_TOKEN_KEY);
|
||||
if (token && isTokenExpired(token)) {
|
||||
refreshToken();
|
||||
}
|
||||
|
||||
if (!refreshTokenValue) {
|
||||
logout();
|
||||
return;
|
||||
}
|
||||
setIsInitializing(false);
|
||||
}, [pathname, refreshToken]);
|
||||
|
||||
try {
|
||||
const response = await client.post<Token>({
|
||||
url: '/api/v1/auth/refresh',
|
||||
body: {refresh_token: refreshTokenValue},
|
||||
});
|
||||
// Protected routes logic
|
||||
useEffect(() => {
|
||||
if (isInitializing) return;
|
||||
|
||||
const newTokens = response.data;
|
||||
const protectedRoutes = [
|
||||
"/admin",
|
||||
"/dashboard",
|
||||
"/events/create",
|
||||
"/events/edit",
|
||||
"/profile",
|
||||
];
|
||||
|
||||
if (newTokens) {
|
||||
// Store new tokens
|
||||
storeToken(newTokens.access_token, newTokens.refresh_token || null);
|
||||
const publicOnlyRoutes = ["/login", "/register"];
|
||||
|
||||
// Update client headers
|
||||
client.setConfig({
|
||||
headers: {
|
||||
Authorization: `Bearer ${newTokens.access_token}`
|
||||
}
|
||||
});
|
||||
const publicRoutes = ["/", "/invite", "/rsvp"];
|
||||
|
||||
}
|
||||
// Refetch user data
|
||||
await refetch();
|
||||
} catch (error) {
|
||||
console.error('Token refresh failed:', error);
|
||||
logout();
|
||||
}
|
||||
}, [logout, refetch]);
|
||||
|
||||
// Check token validity and refresh if needed on route changes
|
||||
useEffect(() => {
|
||||
const token = getStoredToken();
|
||||
|
||||
if (token && isTokenExpired(token)) {
|
||||
refreshToken();
|
||||
}
|
||||
|
||||
setIsInitializing(false);
|
||||
}, [pathname, refreshToken]);
|
||||
|
||||
// Protected routes logic
|
||||
useEffect(() => {
|
||||
if (isInitializing) return;
|
||||
|
||||
const protectedRoutes = [
|
||||
'/admin',
|
||||
'/dashboard',
|
||||
'/events/create',
|
||||
'/events/edit',
|
||||
'/profile',
|
||||
];
|
||||
|
||||
const publicOnlyRoutes = [
|
||||
'/login',
|
||||
'/register',
|
||||
];
|
||||
|
||||
const publicRoutes = [
|
||||
'/',
|
||||
'/invite',
|
||||
'/rsvp',
|
||||
];
|
||||
|
||||
const isProtectedRoute = protectedRoutes.some(route => pathname?.startsWith(route));
|
||||
const isPublicOnlyRoute = publicOnlyRoutes.some(route => pathname === route);
|
||||
|
||||
// Handle loading state
|
||||
if (isLoading && isProtectedRoute) return;
|
||||
|
||||
// Redirect to login if not authenticated but trying to access protected route
|
||||
if (isProtectedRoute && !user) {
|
||||
router.push(`/login?redirect=${encodeURIComponent(pathname || '')}`);
|
||||
}
|
||||
|
||||
// Redirect to dashboard if authenticated but trying to access public-only route
|
||||
if (isPublicOnlyRoute && user) {
|
||||
router.push('/dashboard');
|
||||
}
|
||||
}, [user, isLoading, pathname, router, isInitializing]);
|
||||
|
||||
// Create the context value
|
||||
const value = useMemo<AuthContextState>(() => ({
|
||||
user: user as UserResponse | null,
|
||||
isAuthenticated: !!user,
|
||||
isLoading: isInitializing || isLoading,
|
||||
error: error as Error | null,
|
||||
login,
|
||||
logout,
|
||||
refreshToken,
|
||||
}), [user, isInitializing, isLoading, error, login, logout, refreshToken]);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={value}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
const isProtectedRoute = protectedRoutes.some((route) =>
|
||||
pathname?.startsWith(route),
|
||||
);
|
||||
const isPublicOnlyRoute = publicOnlyRoutes.some(
|
||||
(route) => pathname === route,
|
||||
);
|
||||
};
|
||||
|
||||
// Handle loading state
|
||||
if (isLoading && isProtectedRoute) return;
|
||||
|
||||
// Redirect to login if not authenticated but trying to access protected route
|
||||
if (isProtectedRoute && !user) {
|
||||
router.push(`/login?redirect=${encodeURIComponent(pathname || "")}`);
|
||||
}
|
||||
|
||||
// Redirect to dashboard if authenticated but trying to access public-only route
|
||||
if (isPublicOnlyRoute && user) {
|
||||
router.push("/dashboard");
|
||||
}
|
||||
}, [user, isLoading, pathname, router, isInitializing]);
|
||||
|
||||
// Create the context value
|
||||
const value = useMemo<AuthContextState>(
|
||||
() => ({
|
||||
user: user as UserResponse | null,
|
||||
isAuthenticated: !!user,
|
||||
isLoading: isInitializing || isLoading,
|
||||
error: error as Error | null,
|
||||
login,
|
||||
logout,
|
||||
refreshToken,
|
||||
}),
|
||||
[user, isInitializing, isLoading, error, login, logout, refreshToken],
|
||||
);
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user