Files
clube67_newwhats.local/newwhats.clube67.com/newwhats.local/frontend/context/AuthContext.tsx
T
VPS 4 Deploy Agent 2f8c04a0a7
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
chore(ops): restore all source files in newwhats.clube67.com
2026-05-18 03:28:29 +02:00

149 lines
4.6 KiB
TypeScript

import React, { createContext, useContext, useState, useEffect, useCallback } from 'react'
import { useRouter } from 'next/router'
import { authApi, api } from '../services/chatApiService'
import { socketService } from '../services/socketService'
interface User {
id: string
email: string
name?: string
role: 'user' | 'admin'
daysRemaining?: number | null
planId?: string | null
}
interface AuthContextType {
user: User | null
token: string | null
loading: boolean
login: (token: string, userData: User, refreshToken?: string) => void
logout: () => void
}
const AuthContext = createContext<AuthContextType>({
user: null,
token: null,
loading: true,
login: () => {},
logout: () => {},
})
const PUBLIC_ROUTES = ['/login']
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const [user, setUser] = useState<User | null>(null)
const [token, setToken] = useState<string | null>(null)
const [loading, setLoading] = useState(true)
const router = useRouter()
// ── Valida sessão ao montar ──────────────────────────────────────────────
useEffect(() => {
const init = async () => {
try {
const storedToken = localStorage.getItem('token')
if (!storedToken) return
if (typeof window !== 'undefined') {
document.cookie = `accessToken=${storedToken}; path=/; SameSite=Lax; Secure`
}
setToken(storedToken)
socketService.connect(storedToken)
const me = await authApi.me()
if (me?.id) {
setUser({
id: me.id,
email: me.email,
name: me.name,
role: (me.role ?? 'USER').toLowerCase() as 'user' | 'admin',
daysRemaining: me.daysRemaining ?? null,
planId: me.planId ?? null,
})
} else {
await tryRefresh()
}
} catch {
await tryRefresh()
} finally {
setLoading(false)
}
}
init()
}, [])
// ── Redirect guard ───────────────────────────────────────────────────────
useEffect(() => {
if (loading) return
const isPublic = PUBLIC_ROUTES.some((r) => router.pathname.startsWith(r))
if (!user && !isPublic) router.replace('/login')
}, [loading, user, router.pathname])
// ── Helpers ──────────────────────────────────────────────────────────────
const tryRefresh = async () => {
try {
const refreshToken = localStorage.getItem('refreshToken')
if (!refreshToken) { clearSession(); return }
const { data } = await api.post('/api/auth/refresh', { refreshToken })
localStorage.setItem('token', data.accessToken)
if (typeof window !== 'undefined') {
document.cookie = `accessToken=${data.accessToken}; path=/; SameSite=Lax; Secure`
}
localStorage.setItem('refreshToken', data.refreshToken)
setToken(data.accessToken)
setUser({
id: data.user.id,
email: data.user.email,
name: data.user.name,
role: (data.user.role ?? 'USER').toLowerCase() as 'user' | 'admin',
})
} catch {
clearSession()
}
}
const clearSession = () => {
localStorage.removeItem('token')
localStorage.removeItem('refreshToken')
if (typeof window !== 'undefined') {
document.cookie = 'accessToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'
}
setToken(null)
setUser(null)
}
const login = useCallback((newToken: string, userData: User, refreshToken?: string) => {
localStorage.setItem('token', newToken)
if (typeof window !== 'undefined') {
document.cookie = `accessToken=${newToken}; path=/; SameSite=Lax; Secure`
}
if (refreshToken) localStorage.setItem('refreshToken', refreshToken)
setToken(newToken)
setUser(userData)
socketService.connect(newToken)
router.push(userData.role === 'admin' ? '/admin/dashboard' : '/inbox')
}, [router])
const logout = useCallback(async () => {
try {
await authApi.logout().catch(() => {})
} finally {
socketService.disconnect()
clearSession()
router.replace('/login')
}
}, [router])
return (
<AuthContext.Provider value={{ user, token, loading, login, logout }}>
{children}
</AuthContext.Provider>
)
}
export const useAuth = () => useContext(AuthContext)