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
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(
status_code=status.HTTP_403_FORBIDDEN,
detail="Your account has been banned",
detail=ban_info,
)
# If admin with Telegram linked, require 2FA

View File

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

View File

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

View File

@@ -54,7 +54,8 @@ export function LoginPage() {
navigate('/marathons')
} 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,
login: async (data) => {
set({ isLoading: true, error: null, pending2FA: null })
set({ isLoading: true, error: null, pending2FA: null, banInfo: null })
try {
const response = await authApi.login(data)
@@ -85,9 +85,34 @@ export const useAuthStore = create<AuthState>()(
}
return { requires2FA: false }
} 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({
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,
})
throw err