Fix ban screen

This commit is contained in:
2025-12-20 23:59:13 +07:00
parent 6c824712c9
commit 95e2a77335
5 changed files with 44 additions and 11 deletions

View File

@@ -59,9 +59,15 @@ async def login(request: Request, data: UserLogin, db: DbSession):
# Check if user is banned # Check if user is banned
if user.is_banned: if user.is_banned:
# Return full ban info like in deps.py
ban_info = {
"banned_at": user.banned_at.isoformat() if user.banned_at else None,
"banned_until": user.banned_until.isoformat() if user.banned_until else None,
"reason": user.ban_reason,
}
raise HTTPException( raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, status_code=status.HTTP_403_FORBIDDEN,
detail="Your account has been banned", detail=ban_info,
) )
# If admin with Telegram linked, require 2FA # If admin with Telegram linked, require 2FA

View File

@@ -61,7 +61,6 @@ function PublicRoute({ children }: { children: React.ReactNode }) {
function App() { function App() {
const banInfo = useAuthStore((state) => state.banInfo) const banInfo = useAuthStore((state) => state.banInfo)
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
const syncUser = useAuthStore((state) => state.syncUser) const syncUser = useAuthStore((state) => state.syncUser)
// Sync user data with server on app load // Sync user data with server on app load
@@ -69,8 +68,8 @@ function App() {
syncUser() syncUser()
}, [syncUser]) }, [syncUser])
// Show banned screen if user is authenticated and banned // Show banned screen if user is banned (either authenticated or during login attempt)
if (isAuthenticated && banInfo) { if (banInfo) {
return ( return (
<> <>
<ToastContainer /> <ToastContainer />

View File

@@ -10,6 +10,7 @@ interface BanInfo {
interface BannedScreenProps { interface BannedScreenProps {
banInfo: BanInfo banInfo: BanInfo
onLogout?: () => void
} }
function formatDate(dateStr: string | null) { function formatDate(dateStr: string | null) {
@@ -24,8 +25,9 @@ function formatDate(dateStr: string | null) {
}) + ' (МСК)' }) + ' (МСК)'
} }
export function BannedScreen({ banInfo }: BannedScreenProps) { export function BannedScreen({ banInfo, onLogout }: BannedScreenProps) {
const logout = useAuthStore((state) => state.logout) const storeLogout = useAuthStore((state) => state.logout)
const handleLogout = onLogout || storeLogout
const bannedAtFormatted = formatDate(banInfo.banned_at) const bannedAtFormatted = formatDate(banInfo.banned_at)
const bannedUntilFormatted = formatDate(banInfo.banned_until) const bannedUntilFormatted = formatDate(banInfo.banned_until)
@@ -112,7 +114,7 @@ export function BannedScreen({ banInfo }: BannedScreenProps) {
<NeonButton <NeonButton
variant="secondary" variant="secondary"
size="lg" size="lg"
onClick={logout} onClick={handleLogout}
icon={<LogOut className="w-5 h-5" />} icon={<LogOut className="w-5 h-5" />}
> >
Выйти из аккаунта Выйти из аккаунта

View File

@@ -54,7 +54,8 @@ export function LoginPage() {
navigate('/marathons') navigate('/marathons')
} catch { } catch {
setSubmitError(error || 'Ошибка входа') // Error is already set in store by login function
// Ban case is handled separately via banInfo state
} }
} }

View File

@@ -60,7 +60,7 @@ export const useAuthStore = create<AuthState>()(
banInfo: null, banInfo: null,
login: async (data) => { login: async (data) => {
set({ isLoading: true, error: null, pending2FA: null }) set({ isLoading: true, error: null, pending2FA: null, banInfo: null })
try { try {
const response = await authApi.login(data) const response = await authApi.login(data)
@@ -85,9 +85,34 @@ export const useAuthStore = create<AuthState>()(
} }
return { requires2FA: false } return { requires2FA: false }
} catch (err: unknown) { } catch (err: unknown) {
const error = err as { response?: { data?: { detail?: string } } } const error = err as { response?: { status?: number; data?: { detail?: string | BanInfo } } }
// Check if user is banned (403 with ban info)
if (error.response?.status === 403) {
const detail = error.response?.data?.detail
if (typeof detail === 'object' && detail !== null && 'banned_at' in detail) {
set({ set({
error: error.response?.data?.detail || 'Login failed', banInfo: detail as BanInfo,
isLoading: false,
error: null,
})
throw err
}
}
// Regular error - translate common messages
let errorMessage = 'Ошибка входа'
const detail = error.response?.data?.detail
if (typeof detail === 'string') {
if (detail === 'Incorrect login or password') {
errorMessage = 'Неверный логин или пароль'
} else {
errorMessage = detail
}
}
set({
error: errorMessage,
isLoading: false, isLoading: false,
}) })
throw err throw err