sistema-de-chamados/src/lib/auth-client.tsx

118 lines
3 KiB
TypeScript

"use client"
import { createContext, useContext, useEffect, useMemo, useState } from "react"
import { customSessionClient } from "better-auth/client/plugins"
import { createAuthClient } from "better-auth/react"
import type { AppAuth } from "@/lib/auth"
import { useMutation } from "convex/react"
import { api } from "@/convex/_generated/api"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { isAdmin, isStaff } from "@/lib/authz"
export type AppSession = {
user: {
id: string
name?: string | null
email: string
role: string
tenantId: string | null
avatarUrl: string | null
}
session: {
id: string
expiresAt: number
}
}
const authClient = createAuthClient({
plugins: [customSessionClient<AppAuth>()],
fetchOptions: {
credentials: "include",
},
})
type AuthContextValue = {
session: AppSession | null
isLoading: boolean
convexUserId: string | null
role: string | null
isAdmin: boolean
isStaff: boolean
isCustomer: boolean
}
const AuthContext = createContext<AuthContextValue>({
session: null,
isLoading: true,
convexUserId: null,
role: null,
isAdmin: false,
isStaff: false,
isCustomer: false,
})
export function useAuth() {
return useContext(AuthContext)
}
export const { signIn, signOut, useSession } = authClient
export function AuthProvider({ children }: { children: React.ReactNode }) {
const { data: session, isPending } = useSession()
const ensureUser = useMutation(api.users.ensureUser)
const [convexUserId, setConvexUserId] = useState<string | null>(null)
useEffect(() => {
if (!session?.user) {
setConvexUserId(null)
}
}, [session?.user])
useEffect(() => {
if (!session?.user || convexUserId) return
const controller = new AbortController()
;(async () => {
try {
const ensured = await ensureUser({
tenantId: session.user.tenantId ?? DEFAULT_TENANT_ID,
name: session.user.name ?? session.user.email,
email: session.user.email,
avatarUrl: session.user.avatarUrl ?? undefined,
role: session.user.role.toUpperCase(),
})
if (!controller.signal.aborted) {
setConvexUserId(ensured?._id ?? null)
}
} catch (error) {
if (!controller.signal.aborted) {
console.error("Failed to sync user with Convex", error)
}
}
})()
return () => {
controller.abort()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ensureUser, session?.user?.email, session?.user?.tenantId, session?.user?.role, convexUserId])
const normalizedRole = session?.user?.role ? session.user.role.toLowerCase() : null
const value = useMemo<AuthContextValue>(
() => ({
session: session ?? null,
isLoading: isPending,
convexUserId,
role: normalizedRole,
isAdmin: isAdmin(normalizedRole),
isStaff: isStaff(normalizedRole),
isCustomer: false,
}),
[session, isPending, convexUserId, normalizedRole]
)
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}