Initial commit: full-stack Bun + Elysia + React template
Elysia.js API with session-based auth (email/password + Google OAuth), role system (USER/ADMIN/SUPER_ADMIN), Prisma + PostgreSQL, React 19 with Mantine UI, TanStack Router, dark theme, and comprehensive test suite (unit, integration, E2E with Lightpanda). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
66
src/frontend/hooks/useAuth.ts
Normal file
66
src/frontend/hooks/useAuth.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useNavigate } from '@tanstack/react-router'
|
||||
|
||||
export type Role = 'USER' | 'ADMIN' | 'SUPER_ADMIN'
|
||||
|
||||
export interface User {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
role: Role
|
||||
}
|
||||
|
||||
async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(path, { credentials: 'include', ...init })
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({ error: 'Request failed' }))
|
||||
throw new Error(err.error || `HTTP ${res.status}`)
|
||||
}
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export function useSession() {
|
||||
return useQuery({
|
||||
queryKey: ['auth', 'session'],
|
||||
queryFn: () => apiFetch<{ user: User | null }>('/api/auth/session'),
|
||||
retry: false,
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useLogin() {
|
||||
const queryClient = useQueryClient()
|
||||
const navigate = useNavigate()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: { email: string; password: string }) =>
|
||||
apiFetch<{ user: User }>('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(['auth', 'session'], data)
|
||||
// Super admin → dashboard, others → profile
|
||||
if (data.user.role === 'SUPER_ADMIN') {
|
||||
navigate({ to: '/dashboard' })
|
||||
} else {
|
||||
navigate({ to: '/profile' })
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useLogout() {
|
||||
const queryClient = useQueryClient()
|
||||
const navigate = useNavigate()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: () =>
|
||||
apiFetch<{ ok: boolean }>('/api/auth/logout', { method: 'POST' }),
|
||||
onSuccess: () => {
|
||||
queryClient.setQueryData(['auth', 'session'], { user: null })
|
||||
navigate({ to: '/login' })
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user