- Introduced `useAuthentication` hook to handle session state, PIN verification, and offline mode logic. - Updated `useRouter` to include type definitions for better navigation instance typing. - Refined authentication flow with error handling, user session initialization, and token validation.
223 lines
8.8 KiB
TypeScript
223 lines
8.8 KiB
TypeScript
'use client';
|
|
import {Dispatch, SetStateAction, useContext, useEffect, useState} from 'react';
|
|
import {AppRouterInstance, useRouter} from '@/lib/navigation';
|
|
import {useTranslations} from '@/lib/i18n';
|
|
import {AlertContext, AlertContextProps} from '@/context/AlertContext';
|
|
import {LangContext, LangContextProps} from '@/context/LangContext';
|
|
import OfflineContext, {OfflineMode} from '@/context/OfflineContext';
|
|
import {SessionProps} from '@/lib/types/session';
|
|
import {UserProps} from '@/lib/types/user';
|
|
import {apiGet} from '@/lib/api/client';
|
|
import {isDesktop} from '@/lib/configs';
|
|
import * as tauri from '@/lib/tauri';
|
|
import {getCookie} from '@/lib/utils/cookies';
|
|
|
|
interface UseAuthenticationReturn {
|
|
session: SessionProps;
|
|
setSession: Dispatch<SetStateAction<SessionProps>>;
|
|
isLoading: boolean;
|
|
currentCredits: number;
|
|
setCurrentCredits: Dispatch<SetStateAction<number>>;
|
|
amountSpent: number;
|
|
setAmountSpent: Dispatch<SetStateAction<number>>;
|
|
showPinSetup: boolean;
|
|
setShowPinSetup: Dispatch<SetStateAction<boolean>>;
|
|
showPinVerify: boolean;
|
|
setShowPinVerify: Dispatch<SetStateAction<boolean>>;
|
|
handlePinVerifySuccess: (userId: string) => Promise<void>;
|
|
}
|
|
|
|
export default function useAuthentication(): UseAuthenticationReturn {
|
|
const t = useTranslations();
|
|
const {lang: locale}: LangContextProps = useContext<LangContextProps>(LangContext);
|
|
const {errorMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
|
|
const {initializeDatabase, setOfflineMode} = useContext(OfflineContext);
|
|
const router: AppRouterInstance = useRouter();
|
|
|
|
const [session, setSession] = useState<SessionProps>({user: null, accessToken: '', isConnected: false});
|
|
const [currentCredits, setCurrentCredits] = useState<number>(0);
|
|
const [amountSpent, setAmountSpent] = useState<number>(session.user?.aiUsage || 0);
|
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
const [sessionAttempts, setSessionAttempts] = useState<number>(0);
|
|
const [showPinSetup, setShowPinSetup] = useState<boolean>(false);
|
|
const [showPinVerify, setShowPinVerify] = useState<boolean>(false);
|
|
|
|
useEffect((): void => {
|
|
checkAuthentification().then();
|
|
}, []);
|
|
|
|
useEffect((): void => {
|
|
if (session.isConnected) {
|
|
setIsLoading(false);
|
|
} else {
|
|
if (sessionAttempts > 2) {
|
|
router.push('/');
|
|
}
|
|
}
|
|
setSessionAttempts(sessionAttempts + 1);
|
|
}, [session]);
|
|
|
|
async function checkAuthentification(): Promise<void> {
|
|
const token: string | null = isDesktop ? await tauri.getToken() : getCookie('token');
|
|
|
|
if (token) {
|
|
try {
|
|
const user: UserProps = await apiGet<UserProps>('user/infos', token, locale);
|
|
if (!user) {
|
|
errorMessage(t('homePage.errors.userNotFound'));
|
|
if (isDesktop) {
|
|
await tauri.removeToken();
|
|
await tauri.logout();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (isDesktop && user.id) {
|
|
try {
|
|
const initResult = await tauri.initUser(user.id);
|
|
if (!initResult.success) {
|
|
errorMessage(initResult.error || t('homePage.errors.offlineInitError'));
|
|
return;
|
|
}
|
|
try {
|
|
const offlineStatus = await tauri.offlineModeGet();
|
|
if (!offlineStatus.hasPin && user.termsAccepted) {
|
|
setTimeout((): void => {
|
|
setShowPinSetup(true);
|
|
}, 2000);
|
|
}
|
|
} catch (error) {
|
|
errorMessage(t('homePage.errors.offlineModeError'));
|
|
}
|
|
} catch (error) {
|
|
errorMessage(t('homePage.errors.offlineInitError'));
|
|
}
|
|
|
|
try {
|
|
const dbInitialized: boolean = await initializeDatabase(user.id);
|
|
if (dbInitialized) {
|
|
try {
|
|
await tauri.syncUser({
|
|
userId: user.id,
|
|
username: user.username || '',
|
|
email: user.email || ''
|
|
});
|
|
} catch (syncError) {
|
|
errorMessage(t('homePage.errors.syncError'));
|
|
}
|
|
}
|
|
} catch (error) {
|
|
errorMessage(t('homePage.errors.syncError'));
|
|
}
|
|
}
|
|
|
|
setSession({
|
|
isConnected: true,
|
|
user: user,
|
|
accessToken: token,
|
|
});
|
|
setCurrentCredits(user.creditsBalance);
|
|
setAmountSpent(user.aiUsage);
|
|
} catch (e: unknown) {
|
|
if (isDesktop) {
|
|
try {
|
|
const offlineStatus = await tauri.offlineModeGet();
|
|
if (offlineStatus.hasPin && offlineStatus.lastUserId) {
|
|
setOfflineMode((prev: OfflineMode): OfflineMode => ({
|
|
...prev,
|
|
isOffline: true,
|
|
isNetworkOnline: false
|
|
}));
|
|
setShowPinVerify(true);
|
|
setIsLoading(false);
|
|
return;
|
|
} else {
|
|
await tauri.removeToken();
|
|
await tauri.logout();
|
|
}
|
|
} catch (offlineError) {
|
|
errorMessage(t('homePage.errors.offlineError'));
|
|
}
|
|
} else {
|
|
window.location.href = 'https://eritors.com/login';
|
|
}
|
|
|
|
if (e instanceof Error) {
|
|
errorMessage(e.message);
|
|
} else {
|
|
errorMessage(t('homePage.errors.authenticationError'));
|
|
}
|
|
}
|
|
} else {
|
|
if (isDesktop) {
|
|
try {
|
|
const offlineStatus = await tauri.offlineModeGet();
|
|
if (offlineStatus.hasPin && offlineStatus.lastUserId) {
|
|
setOfflineMode((prev: OfflineMode): OfflineMode => ({
|
|
...prev,
|
|
isOffline: true,
|
|
isNetworkOnline: false
|
|
}));
|
|
setShowPinVerify(true);
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
} catch (error) {
|
|
errorMessage(t('homePage.errors.authenticationError'));
|
|
}
|
|
await tauri.openLoginWindow();
|
|
} else {
|
|
window.location.href = 'https://eritors.com/login';
|
|
}
|
|
}
|
|
}
|
|
|
|
async function handlePinVerifySuccess(userId: string): Promise<void> {
|
|
try {
|
|
const storedToken: string | null = await tauri.getToken();
|
|
const encryptionKey: string | null = await tauri.getUserEncryptionKey(userId);
|
|
|
|
if (encryptionKey) {
|
|
await tauri.dbInitialize(userId, encryptionKey);
|
|
setOfflineMode((prev: OfflineMode): OfflineMode => ({
|
|
...prev,
|
|
isDatabaseInitialized: true
|
|
}));
|
|
|
|
const localUser: UserProps = await tauri.getUserInfo();
|
|
if (localUser && localUser.id) {
|
|
setSession({
|
|
isConnected: true,
|
|
user: localUser,
|
|
accessToken: storedToken || '',
|
|
});
|
|
setShowPinVerify(false);
|
|
setCurrentCredits(localUser.creditsBalance || 0);
|
|
setAmountSpent(localUser.aiUsage || 0);
|
|
} else {
|
|
errorMessage(t('homePage.errors.localDataError'));
|
|
}
|
|
} else {
|
|
errorMessage(t('homePage.errors.encryptionKeyError'));
|
|
}
|
|
} catch (error) {
|
|
errorMessage(t('homePage.errors.offlineModeError'));
|
|
}
|
|
}
|
|
|
|
return {
|
|
session,
|
|
setSession,
|
|
isLoading,
|
|
currentCredits,
|
|
setCurrentCredits,
|
|
amountSpent,
|
|
setAmountSpent,
|
|
showPinSetup,
|
|
setShowPinSetup,
|
|
showPinVerify,
|
|
setShowPinVerify,
|
|
handlePinVerifySuccess,
|
|
};
|
|
}
|