mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
refactor: migrate to eslint flat config and format codebase with prettier
- Remove legacy .eslintrc.cjs and .eslintignore - Add eslint.config.js with flat config, security rules (no-eval, no-implied-eval, no-new-func, no-script-url) - Add .prettierrc and .prettierignore - Format entire codebase with prettier
This commit is contained in:
@@ -1,39 +1,44 @@
|
||||
import { create } from 'zustand'
|
||||
import { persist } from 'zustand/middleware'
|
||||
import type { RegisterResponse, User } from '../types'
|
||||
import { authApi } from '../api/auth'
|
||||
import { apiClient } from '../api/client'
|
||||
import { tokenStorage, isTokenValid, tokenRefreshManager } from '../utils/token'
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { RegisterResponse, User } from '../types';
|
||||
import { authApi } from '../api/auth';
|
||||
import { apiClient } from '../api/client';
|
||||
import { tokenStorage, isTokenValid, tokenRefreshManager } from '../utils/token';
|
||||
|
||||
export interface TelegramWidgetData {
|
||||
id: number
|
||||
first_name: string
|
||||
last_name?: string
|
||||
username?: string
|
||||
photo_url?: string
|
||||
auth_date: number
|
||||
hash: string
|
||||
id: number;
|
||||
first_name: string;
|
||||
last_name?: string;
|
||||
username?: string;
|
||||
photo_url?: string;
|
||||
auth_date: number;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
interface AuthState {
|
||||
accessToken: string | null
|
||||
refreshToken: string | null
|
||||
user: User | null
|
||||
isAuthenticated: boolean
|
||||
isLoading: boolean
|
||||
isAdmin: boolean
|
||||
accessToken: string | null;
|
||||
refreshToken: string | null;
|
||||
user: User | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
isAdmin: boolean;
|
||||
|
||||
setTokens: (accessToken: string, refreshToken: string) => void
|
||||
setUser: (user: User) => void
|
||||
setIsAdmin: (isAdmin: boolean) => void
|
||||
logout: () => void
|
||||
initialize: () => Promise<void>
|
||||
refreshUser: () => Promise<void>
|
||||
checkAdminStatus: () => Promise<void>
|
||||
loginWithTelegram: (initData: string) => Promise<void>
|
||||
loginWithTelegramWidget: (data: TelegramWidgetData) => Promise<void>
|
||||
loginWithEmail: (email: string, password: string) => Promise<void>
|
||||
registerWithEmail: (email: string, password: string, firstName?: string, referralCode?: string) => Promise<RegisterResponse>
|
||||
setTokens: (accessToken: string, refreshToken: string) => void;
|
||||
setUser: (user: User) => void;
|
||||
setIsAdmin: (isAdmin: boolean) => void;
|
||||
logout: () => void;
|
||||
initialize: () => Promise<void>;
|
||||
refreshUser: () => Promise<void>;
|
||||
checkAdminStatus: () => Promise<void>;
|
||||
loginWithTelegram: (initData: string) => Promise<void>;
|
||||
loginWithTelegramWidget: (data: TelegramWidgetData) => Promise<void>;
|
||||
loginWithEmail: (email: string, password: string) => Promise<void>;
|
||||
registerWithEmail: (
|
||||
email: string,
|
||||
password: string,
|
||||
firstName?: string,
|
||||
referralCode?: string,
|
||||
) => Promise<RegisterResponse>;
|
||||
}
|
||||
|
||||
// Блокировка для предотвращения race condition при инициализации
|
||||
@@ -42,7 +47,7 @@ const initState = {
|
||||
promise: null as Promise<void> | null,
|
||||
isInitializing: false,
|
||||
isInitialized: false,
|
||||
}
|
||||
};
|
||||
|
||||
export const useAuthStore = create<AuthState>()(
|
||||
persist(
|
||||
@@ -55,59 +60,59 @@ export const useAuthStore = create<AuthState>()(
|
||||
isAdmin: false,
|
||||
|
||||
setTokens: (accessToken, refreshToken) => {
|
||||
tokenStorage.setTokens(accessToken, refreshToken)
|
||||
tokenStorage.setTokens(accessToken, refreshToken);
|
||||
set({
|
||||
accessToken,
|
||||
refreshToken,
|
||||
isAuthenticated: true,
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
setUser: (user) => {
|
||||
set({ user })
|
||||
set({ user });
|
||||
},
|
||||
|
||||
setIsAdmin: (isAdmin) => {
|
||||
set({ isAdmin })
|
||||
set({ isAdmin });
|
||||
},
|
||||
|
||||
logout: () => {
|
||||
// Get refresh token from secure storage, not zustand state
|
||||
const refreshToken = tokenStorage.getRefreshToken()
|
||||
const refreshToken = tokenStorage.getRefreshToken();
|
||||
if (refreshToken) {
|
||||
authApi.logout(refreshToken).catch(() => {
|
||||
// Logout API call failed - ignore silently
|
||||
})
|
||||
});
|
||||
}
|
||||
tokenStorage.clearTokens()
|
||||
tokenStorage.clearTokens();
|
||||
set({
|
||||
accessToken: null,
|
||||
refreshToken: null,
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
isAdmin: false,
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
checkAdminStatus: async () => {
|
||||
try {
|
||||
const token = tokenStorage.getAccessToken()
|
||||
const token = tokenStorage.getAccessToken();
|
||||
if (!token || !isTokenValid(token)) {
|
||||
set({ isAdmin: false })
|
||||
return
|
||||
set({ isAdmin: false });
|
||||
return;
|
||||
}
|
||||
// Используем apiClient для единообразной обработки ошибок
|
||||
const response = await apiClient.get<{ is_admin: boolean }>('/cabinet/auth/me/is-admin')
|
||||
set({ isAdmin: response.data.is_admin })
|
||||
const response = await apiClient.get<{ is_admin: boolean }>('/cabinet/auth/me/is-admin');
|
||||
set({ isAdmin: response.data.is_admin });
|
||||
} catch {
|
||||
set({ isAdmin: false })
|
||||
set({ isAdmin: false });
|
||||
}
|
||||
},
|
||||
|
||||
refreshUser: async () => {
|
||||
try {
|
||||
const user = await authApi.getMe()
|
||||
set({ user })
|
||||
const user = await authApi.getMe();
|
||||
set({ user });
|
||||
} catch {
|
||||
// Failed to refresh user - ignore silently
|
||||
}
|
||||
@@ -116,150 +121,150 @@ export const useAuthStore = create<AuthState>()(
|
||||
initialize: async () => {
|
||||
// Защита от race condition - если уже инициализировано, выходим
|
||||
if (initState.isInitialized) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// Если уже идёт инициализация, ждём её завершения
|
||||
if (initState.isInitializing && initState.promise) {
|
||||
return initState.promise
|
||||
return initState.promise;
|
||||
}
|
||||
|
||||
initState.isInitializing = true
|
||||
initState.isInitializing = true;
|
||||
initState.promise = (async () => {
|
||||
try {
|
||||
set({ isLoading: true })
|
||||
set({ isLoading: true });
|
||||
|
||||
// Миграция токенов из localStorage (для обратной совместимости)
|
||||
tokenStorage.migrateFromLocalStorage()
|
||||
tokenStorage.migrateFromLocalStorage();
|
||||
|
||||
const accessToken = tokenStorage.getAccessToken()
|
||||
const refreshToken = tokenStorage.getRefreshToken()
|
||||
const accessToken = tokenStorage.getAccessToken();
|
||||
const refreshToken = tokenStorage.getRefreshToken();
|
||||
|
||||
if (!accessToken || !refreshToken) {
|
||||
set({ isLoading: false, isAuthenticated: false })
|
||||
return
|
||||
set({ isLoading: false, isAuthenticated: false });
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверяем валидность токена перед использованием
|
||||
if (!isTokenValid(accessToken)) {
|
||||
// Используем централизованный менеджер для refresh
|
||||
const newToken = await tokenRefreshManager.refreshAccessToken()
|
||||
const newToken = await tokenRefreshManager.refreshAccessToken();
|
||||
if (newToken) {
|
||||
const user = await authApi.getMe()
|
||||
const user = await authApi.getMe();
|
||||
// Сначала проверяем admin статус, потом снимаем isLoading
|
||||
await get().checkAdminStatus()
|
||||
await get().checkAdminStatus();
|
||||
set({
|
||||
accessToken: newToken,
|
||||
refreshToken,
|
||||
user,
|
||||
isAuthenticated: true,
|
||||
isLoading: false,
|
||||
})
|
||||
});
|
||||
} else {
|
||||
tokenStorage.clearTokens()
|
||||
tokenStorage.clearTokens();
|
||||
set({
|
||||
accessToken: null,
|
||||
refreshToken: null,
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
isLoading: false,
|
||||
})
|
||||
});
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await authApi.getMe()
|
||||
const user = await authApi.getMe();
|
||||
// Сначала проверяем admin статус, потом снимаем isLoading
|
||||
await get().checkAdminStatus()
|
||||
await get().checkAdminStatus();
|
||||
set({
|
||||
accessToken,
|
||||
refreshToken,
|
||||
user,
|
||||
isAuthenticated: true,
|
||||
isLoading: false,
|
||||
})
|
||||
});
|
||||
} catch {
|
||||
// Token might be invalid on server, try to refresh
|
||||
const newToken = await tokenRefreshManager.refreshAccessToken()
|
||||
const newToken = await tokenRefreshManager.refreshAccessToken();
|
||||
if (newToken) {
|
||||
try {
|
||||
const user = await authApi.getMe()
|
||||
const user = await authApi.getMe();
|
||||
// Сначала проверяем admin статус, потом снимаем isLoading
|
||||
await get().checkAdminStatus()
|
||||
await get().checkAdminStatus();
|
||||
set({
|
||||
accessToken: newToken,
|
||||
refreshToken,
|
||||
user,
|
||||
isAuthenticated: true,
|
||||
isLoading: false,
|
||||
})
|
||||
});
|
||||
} catch {
|
||||
tokenStorage.clearTokens()
|
||||
tokenStorage.clearTokens();
|
||||
set({
|
||||
accessToken: null,
|
||||
refreshToken: null,
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
isLoading: false,
|
||||
})
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Refresh failed, logout
|
||||
tokenStorage.clearTokens()
|
||||
tokenStorage.clearTokens();
|
||||
set({
|
||||
accessToken: null,
|
||||
refreshToken: null,
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
isLoading: false,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
initState.isInitializing = false
|
||||
initState.isInitialized = true
|
||||
initState.promise = null
|
||||
initState.isInitializing = false;
|
||||
initState.isInitialized = true;
|
||||
initState.promise = null;
|
||||
}
|
||||
})()
|
||||
})();
|
||||
|
||||
return initState.promise
|
||||
return initState.promise;
|
||||
},
|
||||
|
||||
loginWithTelegram: async (initData) => {
|
||||
const response = await authApi.loginTelegram(initData)
|
||||
tokenStorage.setTokens(response.access_token, response.refresh_token)
|
||||
const response = await authApi.loginTelegram(initData);
|
||||
tokenStorage.setTokens(response.access_token, response.refresh_token);
|
||||
set({
|
||||
accessToken: response.access_token,
|
||||
refreshToken: response.refresh_token,
|
||||
user: response.user,
|
||||
isAuthenticated: true,
|
||||
})
|
||||
get().checkAdminStatus()
|
||||
});
|
||||
get().checkAdminStatus();
|
||||
},
|
||||
|
||||
loginWithTelegramWidget: async (data) => {
|
||||
const response = await authApi.loginTelegramWidget(data)
|
||||
tokenStorage.setTokens(response.access_token, response.refresh_token)
|
||||
const response = await authApi.loginTelegramWidget(data);
|
||||
tokenStorage.setTokens(response.access_token, response.refresh_token);
|
||||
set({
|
||||
accessToken: response.access_token,
|
||||
refreshToken: response.refresh_token,
|
||||
user: response.user,
|
||||
isAuthenticated: true,
|
||||
})
|
||||
get().checkAdminStatus()
|
||||
});
|
||||
get().checkAdminStatus();
|
||||
},
|
||||
|
||||
loginWithEmail: async (email, password) => {
|
||||
const response = await authApi.loginEmail(email, password)
|
||||
tokenStorage.setTokens(response.access_token, response.refresh_token)
|
||||
const response = await authApi.loginEmail(email, password);
|
||||
tokenStorage.setTokens(response.access_token, response.refresh_token);
|
||||
set({
|
||||
accessToken: response.access_token,
|
||||
refreshToken: response.refresh_token,
|
||||
user: response.user,
|
||||
isAuthenticated: true,
|
||||
})
|
||||
get().checkAdminStatus()
|
||||
});
|
||||
get().checkAdminStatus();
|
||||
},
|
||||
|
||||
registerWithEmail: async (email, password, firstName, referralCode) => {
|
||||
@@ -271,8 +276,8 @@ export const useAuthStore = create<AuthState>()(
|
||||
first_name: firstName,
|
||||
language: navigator.language.split('-')[0] || 'ru',
|
||||
referral_code: referralCode,
|
||||
})
|
||||
return response
|
||||
});
|
||||
return response;
|
||||
},
|
||||
}),
|
||||
{
|
||||
@@ -282,9 +287,9 @@ export const useAuthStore = create<AuthState>()(
|
||||
partialize: (state) => ({
|
||||
user: state.user,
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Initialize auth on app load
|
||||
useAuthStore.getState().initialize()
|
||||
useAuthStore.getState().initialize();
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import { create } from 'zustand'
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type BlockingType = 'maintenance' | 'channel_subscription' | null
|
||||
export type BlockingType = 'maintenance' | 'channel_subscription' | null;
|
||||
|
||||
interface MaintenanceInfo {
|
||||
message: string
|
||||
reason?: string
|
||||
message: string;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
interface ChannelSubscriptionInfo {
|
||||
message: string
|
||||
channel_link?: string
|
||||
message: string;
|
||||
channel_link?: string;
|
||||
}
|
||||
|
||||
interface BlockingState {
|
||||
blockingType: BlockingType
|
||||
maintenanceInfo: MaintenanceInfo | null
|
||||
channelInfo: ChannelSubscriptionInfo | null
|
||||
blockingType: BlockingType;
|
||||
maintenanceInfo: MaintenanceInfo | null;
|
||||
channelInfo: ChannelSubscriptionInfo | null;
|
||||
|
||||
setMaintenance: (info: MaintenanceInfo) => void
|
||||
setChannelSubscription: (info: ChannelSubscriptionInfo) => void
|
||||
clearBlocking: () => void
|
||||
setMaintenance: (info: MaintenanceInfo) => void;
|
||||
setChannelSubscription: (info: ChannelSubscriptionInfo) => void;
|
||||
clearBlocking: () => void;
|
||||
}
|
||||
|
||||
export const useBlockingStore = create<BlockingState>((set) => ({
|
||||
@@ -27,21 +27,24 @@ export const useBlockingStore = create<BlockingState>((set) => ({
|
||||
maintenanceInfo: null,
|
||||
channelInfo: null,
|
||||
|
||||
setMaintenance: (info) => set({
|
||||
blockingType: 'maintenance',
|
||||
maintenanceInfo: info,
|
||||
channelInfo: null,
|
||||
}),
|
||||
setMaintenance: (info) =>
|
||||
set({
|
||||
blockingType: 'maintenance',
|
||||
maintenanceInfo: info,
|
||||
channelInfo: null,
|
||||
}),
|
||||
|
||||
setChannelSubscription: (info) => set({
|
||||
blockingType: 'channel_subscription',
|
||||
channelInfo: info,
|
||||
maintenanceInfo: null,
|
||||
}),
|
||||
setChannelSubscription: (info) =>
|
||||
set({
|
||||
blockingType: 'channel_subscription',
|
||||
channelInfo: info,
|
||||
maintenanceInfo: null,
|
||||
}),
|
||||
|
||||
clearBlocking: () => set({
|
||||
blockingType: null,
|
||||
maintenanceInfo: null,
|
||||
channelInfo: null,
|
||||
}),
|
||||
}))
|
||||
clearBlocking: () =>
|
||||
set({
|
||||
blockingType: null,
|
||||
maintenanceInfo: null,
|
||||
channelInfo: null,
|
||||
}),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user