refactor-upto-phase 6

This commit is contained in:
alorig
2025-11-20 21:29:14 +05:00
parent 8b798ed191
commit b0409d965b
14 changed files with 478 additions and 314 deletions

View File

@@ -6,6 +6,14 @@ import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { fetchAPI } from '../services/api';
type AuthErrorCode = 'ACCOUNT_REQUIRED' | 'PLAN_REQUIRED' | 'AUTH_FAILED';
function createAuthError(message: string, code: AuthErrorCode): Error & { code: AuthErrorCode } {
const error = new Error(message) as Error & { code: AuthErrorCode };
error.code = code;
return error;
}
interface User {
id: number;
email: string;
@@ -60,15 +68,31 @@ export const useAuthStore = create<AuthState>()(
const data = await response.json();
if (!response.ok || !data.success) {
throw new Error(data.error || data.message || 'Login failed');
const message = data.error || data.message || 'Login failed';
if (response.status === 402) {
throw createAuthError(message, 'PLAN_REQUIRED');
}
if (response.status === 403) {
throw createAuthError(message, 'ACCOUNT_REQUIRED');
}
throw createAuthError(message, 'AUTH_FAILED');
}
// Store user and JWT tokens (handle both old and new API formats)
const responseData = data.data || data;
// Support both formats: new (access/refresh at top level) and old (tokens.access/refresh)
const tokens = responseData.tokens || {};
const userData = responseData.user || data.user;
if (!userData?.account) {
throw createAuthError('Account not configured for this user. Please contact support.', 'ACCOUNT_REQUIRED');
}
if (!userData.account.plan) {
throw createAuthError('Active subscription required. Visit igny8.com/pricing to subscribe.', 'PLAN_REQUIRED');
}
set({
user: responseData.user || data.user,
user: userData,
token: responseData.access || tokens.access || data.access || null,
refreshToken: responseData.refresh || tokens.refresh || data.refresh || null,
isAuthenticated: true,
@@ -196,23 +220,24 @@ export const useAuthStore = create<AuthState>()(
}
try {
// Use fetchAPI which handles token automatically and extracts data from unified format
// fetchAPI is already imported at the top of the file
const response = await fetchAPI('/v1/auth/me/');
// fetchAPI extracts data field, so response is {user: {...}}
if (!response || !response.user) {
throw new Error('Failed to refresh user data');
}
// Update user data with latest from server
// This ensures account/plan changes are reflected immediately
set({ user: response.user });
const refreshedUser = response.user;
if (!refreshedUser.account) {
throw createAuthError('Account not configured for this user. Please contact support.', 'ACCOUNT_REQUIRED');
}
if (!refreshedUser.account.plan) {
throw createAuthError('Active subscription required. Visit igny8.com/pricing to subscribe.', 'PLAN_REQUIRED');
}
set({ user: refreshedUser, isAuthenticated: true });
} catch (error: any) {
// If refresh fails, don't logout - just log the error
// User might still be authenticated, just couldn't refresh data
console.warn('Failed to refresh user data:', error);
// Don't throw - just log the warning to prevent error accumulation
set({ user: null, token: null, refreshToken: null, isAuthenticated: false });
throw error;
}
},
}),