72 行
1.6 KiB
TypeScript
72 行
1.6 KiB
TypeScript
import { apiGet, apiPost, isApiError } from '@/lib/api'
|
|
|
|
import type { User, UserRole } from '@/types'
|
|
|
|
const CONTENT_EDITOR_ROLES: readonly UserRole[] = ['admin', 'member']
|
|
let pendingSession: Promise<User> | null = null
|
|
|
|
|
|
const storedUserCode = (): string | null => {
|
|
try
|
|
{
|
|
return localStorage.getItem ('user_code')
|
|
}
|
|
catch
|
|
{
|
|
return null
|
|
}
|
|
}
|
|
|
|
|
|
export const rememberUserCode = (code: string): void => {
|
|
try
|
|
{
|
|
localStorage.setItem ('user_code', code)
|
|
}
|
|
catch
|
|
{
|
|
// A missing recovery copy must not invalidate the authenticated Cookie.
|
|
}
|
|
}
|
|
|
|
|
|
const restoreUserSession = async (): Promise<User> => {
|
|
try
|
|
{
|
|
return await apiGet<User> ('/users/me')
|
|
}
|
|
catch (error)
|
|
{
|
|
if (!isApiError (error) || error.response?.status !== 401)
|
|
throw error
|
|
}
|
|
|
|
const code = storedUserCode ()
|
|
if (code)
|
|
{
|
|
const data = await apiPost<{ valid: boolean; user: User }> (
|
|
'/users/verify', { code, restore: true })
|
|
if (data.valid)
|
|
return data.user
|
|
}
|
|
const data = await apiPost<{ code: string; user: User }> ('/users')
|
|
return data.user
|
|
}
|
|
|
|
|
|
export const initialiseUserSession = (): Promise<User> => {
|
|
if (typeof window === 'undefined')
|
|
throw new Error ('Browser session initialisation is client-only')
|
|
pendingSession ??= restoreUserSession ().then (user => {
|
|
rememberUserCode (user.inheritanceCode)
|
|
return user
|
|
}).finally (() => {
|
|
pendingSession = null
|
|
})
|
|
return pendingSession
|
|
}
|
|
|
|
|
|
export const canEditContent = (
|
|
user: Pick<User, 'role'> | null | undefined): boolean => user != null && CONTENT_EDITOR_ROLES.includes (user.role)
|