init project

This commit is contained in:
root
2026-04-23 00:21:02 -04:00
parent 23826c5528
commit 9191fd32f0
64 changed files with 10229 additions and 0 deletions
+131
View File
@@ -0,0 +1,131 @@
import {
createContext,
useCallback,
useContext,
useMemo,
useSyncExternalStore,
} from 'react'
import { setStoredToken, getStoredToken } from '../api/http'
import type { AuthUser } from '../api/types'
import { loginRequest } from '../api/session'
export type { AuthUser }
type AuthState = {
token: string | null
user: AuthUser | null
}
const STORAGE_USER = 'alrahma_api_user'
function readUser(): AuthUser | null {
try {
const raw = localStorage.getItem(STORAGE_USER)
if (!raw) return null
return JSON.parse(raw) as AuthUser
} catch {
return null
}
}
function writeUser(user: AuthUser | null): void {
if (user) localStorage.setItem(STORAGE_USER, JSON.stringify(user))
else localStorage.removeItem(STORAGE_USER)
}
let memory: AuthState = {
token: getStoredToken(),
user: readUser(),
}
const listeners = new Set<() => void>()
function emit(): void {
listeners.forEach((fn) => fn())
}
function setAuth(next: AuthState): void {
memory = next
emit()
}
function subscribe(listener: () => void): () => void {
listeners.add(listener)
return () => listeners.delete(listener)
}
function getSnapshot(): AuthState {
return memory
}
export function hydrateAuthFromStorage(): void {
memory = {
token: getStoredToken(),
user: readUser(),
}
emit()
}
export function AuthProvider({ children }: { children: React.ReactNode }) {
const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
const login = useCallback(async (email: string, password: string) => {
const result = await loginRequest(email, password)
if (result.status === false) {
return {
ok: false as const,
message: result.message ?? 'Login failed.',
}
}
setStoredToken(result.token)
writeUser(result.user)
setAuth({ token: result.token, user: result.user })
return { ok: true as const }
}, [])
const logout = useCallback(() => {
setStoredToken(null)
writeUser(null)
setAuth({ token: null, user: null })
}, [])
const setSession = useCallback((token: string, user: AuthUser) => {
setStoredToken(token)
writeUser(user)
setAuth({ token, user })
}, [])
const value = useMemo(
() => ({
login,
logout,
setSession,
token: state.token,
user: state.user,
isAuthenticated: Boolean(state.token && state.user),
}),
[state.token, state.user, login, logout, setSession],
)
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
type AuthContextValue = {
login: (
email: string,
password: string,
) => Promise<{ ok: true } | { ok: false; message: string }>
logout: () => void
setSession: (token: string, user: AuthUser) => void
token: string | null
user: AuthUser | null
isAuthenticated: boolean
}
const AuthContext = createContext<AuthContextValue | null>(null)
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext)
if (!ctx) throw new Error('useAuth must be used within AuthProvider')
return ctx
}