Files
clube67_newwhats.local/newwhats.clube67.com/newwhats.local/frontend/store/authStore.ts
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

124 lines
3.7 KiB
TypeScript

/**
* authStore.ts — Estado de autenticação global via Zustand.
* O AuthContext (Next.js) permanece como provider de redirect guard.
* Este store é usado pelos demais stores (chatStore, etc.) sem hook drilling.
*/
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import { authApi } from '../services/chatApiService'
import { socketService } from '../services/socketService'
export interface AuthUser {
id: string
email: string
name: string | null
role: 'USER' | 'ADMIN'
planId: string | null
daysRemaining: number | null
}
interface AuthState {
user: AuthUser | null
token: string | null
refreshToken: string | null
isHydrated: boolean
// Actions
setAuth: (token: string, refreshToken: string, user: AuthUser) => void
clearAuth: () => void
refreshSession: () => Promise<boolean>
fetchMe: () => Promise<void>
}
export const useAuthStore = create<AuthState>()(
persist(
(set, get) => ({
user: null,
token: null,
refreshToken: null,
isHydrated: false,
setAuth: (token, refreshToken, user) => {
localStorage.setItem('token', token)
localStorage.setItem('refreshToken', refreshToken)
if (typeof window !== 'undefined') {
document.cookie = `accessToken=${token}; path=/; SameSite=Lax; Secure`
}
socketService.connect(token)
set({ token, refreshToken, user })
},
clearAuth: () => {
localStorage.removeItem('token')
localStorage.removeItem('refreshToken')
if (typeof window !== 'undefined') {
document.cookie = 'accessToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'
}
socketService.disconnect()
set({ token: null, refreshToken: null, user: null })
},
refreshSession: async () => {
const { refreshToken } = get()
if (!refreshToken) return false
try {
const { data } = await import('axios').then(({ default: axios }) =>
axios.post(
`${process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:8008'}/api/auth/refresh`,
{ refreshToken }
)
)
get().setAuth(data.accessToken, data.refreshToken, {
id: data.user.id,
email: data.user.email,
name: data.user.name ?? null,
role: data.user.role as 'USER' | 'ADMIN',
planId: data.user.planId ?? null,
daysRemaining: data.user.daysRemaining ?? null,
})
return true
} catch {
get().clearAuth()
return false
}
},
fetchMe: async () => {
try {
const me = await authApi.me()
if (me?.id) {
set({
user: {
id: me.id,
email: me.email,
name: me.name ?? null,
role: (me.role ?? 'USER') as 'USER' | 'ADMIN',
planId: me.planId ?? null,
daysRemaining: me.daysRemaining ?? null,
},
})
}
} catch {
// token inválido — clearAuth será chamado pelo interceptor 401
}
},
}),
{
name: 'newwhats:auth',
partialize: (s) => ({ token: s.token, refreshToken: s.refreshToken, user: s.user }),
onRehydrateStorage: () => (state) => {
if (state) {
state.isHydrated = true
// Reconecta socket ao restaurar sessão do localStorage
if (state.token) {
socketService.connect(state.token)
if (typeof window !== 'undefined') {
document.cookie = `accessToken=${state.token}; path=/; SameSite=Lax; Secure`
}
}
}
},
}
)
)