Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | 21x 21x 21x 4x 4x 4x 2x 2x 1x 1x 1x 2x 21x 37x 37x 2x 2x 2x 2x 2x 2x 2x 2x 2x 37x 3x 1x 1x 2x 2x 37x 394x 394x 394x | import { createContext, useContext, useState } from 'react'
import type { ReactNode } from 'react'
import { errorMessage } from './apiError'
export const TOKEN_KEY = 'auth_token'
const USERNAME_KEY = 'auth_username'
const API_BASE = import.meta.env.VITE_API_BASE ?? ''
async function loginRequest(username: string, password: string): Promise<string> {
Iif (!username.trim() || !password) throw new Error('Enter a username and password')
const res = await fetch(`${API_BASE}/api/v1/users/login`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ username, password }),
})
if (!res.ok) throw new Error(await errorMessage(res))
const data = (await res.json()) as { token: string }
return data.token
}
async function registerRequest(username: string, password: string): Promise<void> {
Iif (!username.trim() || !password) throw new Error('Enter a username and password')
const res = await fetch(`${API_BASE}/api/v1/users/register`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ username, password }),
})
Iif (!res.ok) throw new Error(await errorMessage(res))
}
async function logoutRequest(token: string): Promise<void> {
await fetch(`${API_BASE}/api/v1/users/logout`, {
method: 'POST',
headers: { authorization: `Bearer ${token}` },
})
}
type AuthContextValue = {
token: string | null
username: string | null
signIn: (username: string, password: string) => Promise<void>
register: (username: string, password: string) => Promise<void>
updateUsername: (username: string) => void
signOut: () => void
}
const AuthContext = createContext<AuthContextValue | null>(null)
export function AuthProvider({ children }: { children: ReactNode }) {
const [token, setToken] = useState<string | null>(() => localStorage.getItem(TOKEN_KEY))
const [username, setUsername] = useState<string | null>(() => localStorage.getItem(USERNAME_KEY))
function persistSession(newToken: string, newUsername: string) {
localStorage.setItem(TOKEN_KEY, newToken)
localStorage.setItem(USERNAME_KEY, newUsername)
setToken(newToken)
setUsername(newUsername)
}
function clearSession() {
localStorage.removeItem(TOKEN_KEY)
localStorage.removeItem(USERNAME_KEY)
sessionStorage.clear()
setToken(null)
setUsername(null)
}
const value: AuthContextValue = {
token,
username,
signIn: async (u, p) => persistSession(await loginRequest(u, p), u),
register: async (u, p) => {
await registerRequest(u, p)
persistSession(await loginRequest(u, p), u)
},
updateUsername: (u) => {
if (token) persistSession(token, u)
},
signOut: () => {
Eif (token) void logoutRequest(token).catch(() => {})
clearSession()
},
}
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
// eslint-disable-next-line react-refresh/only-export-components
export function useAuth() {
const ctx = useContext(AuthContext)
Iif (!ctx) throw new Error('useAuth must be used within AuthProvider')
return ctx
}
|