101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
const AUTH_STATE_COOKIE = 'magent_logged_in'
|
|
|
|
export const getApiBase = () => process.env.NEXT_PUBLIC_API_BASE ?? '/api'
|
|
|
|
const setCookie = (name: string, value: string, maxAgeSeconds: number) => {
|
|
if (typeof document === 'undefined') return
|
|
document.cookie = `${name}=${value}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`
|
|
}
|
|
|
|
const clearCookie = (name: string) => {
|
|
if (typeof document === 'undefined') return
|
|
document.cookie = `${name}=; Max-Age=0; Path=/; SameSite=Lax`
|
|
}
|
|
|
|
export const getToken = () => {
|
|
if (typeof document === 'undefined') return null
|
|
const cookies = document.cookie.split(';').map((entry) => entry.trim())
|
|
const marker = cookies.find((entry) => entry.startsWith(`${AUTH_STATE_COOKIE}=`))
|
|
if (!marker) return null
|
|
const [, value] = marker.split('=', 2)
|
|
return value || null
|
|
}
|
|
|
|
export const setToken = (_token: string) => {
|
|
setCookie(AUTH_STATE_COOKIE, '1', 60 * 60 * 12)
|
|
}
|
|
|
|
export const clearToken = () => {
|
|
clearCookie(AUTH_STATE_COOKIE)
|
|
if (typeof window === 'undefined') return
|
|
const baseUrl = getApiBase()
|
|
void fetch(`${baseUrl}/auth/logout`, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
keepalive: true,
|
|
}).catch(() => undefined)
|
|
}
|
|
|
|
export const logout = async () => {
|
|
const baseUrl = getApiBase()
|
|
clearCookie(AUTH_STATE_COOKIE)
|
|
await fetch(`${baseUrl}/auth/logout`, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
})
|
|
}
|
|
|
|
export const authFetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
|
const headers = new Headers(init?.headers || {})
|
|
return fetch(input, { ...init, headers, credentials: 'include' })
|
|
}
|
|
|
|
export const getEventStreamToken = async () => {
|
|
const baseUrl = getApiBase()
|
|
const response = await authFetch(`${baseUrl}/auth/stream-token`)
|
|
if (!response.ok) {
|
|
const text = await response.text()
|
|
throw new Error(text || `Stream token request failed: ${response.status}`)
|
|
}
|
|
const data = await response.json()
|
|
const token = typeof data?.stream_token === 'string' ? data.stream_token : ''
|
|
if (!token) {
|
|
throw new Error('Stream token not returned')
|
|
}
|
|
return token
|
|
}
|
|
|
|
export class UnauthorizedError extends Error {
|
|
constructor() {
|
|
super('Unauthorized')
|
|
this.name = 'UnauthorizedError'
|
|
}
|
|
}
|
|
|
|
export class ForbiddenError extends Error {
|
|
constructor() {
|
|
super('Forbidden')
|
|
this.name = 'ForbiddenError'
|
|
}
|
|
}
|
|
|
|
export const authFetchOrThrow = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
const response = await authFetch(input, init)
|
|
if (response.status === 401) {
|
|
clearToken()
|
|
throw new UnauthorizedError()
|
|
}
|
|
if (response.status === 403) {
|
|
throw new ForbiddenError()
|
|
}
|
|
return response
|
|
}
|
|
|
|
export const readResponseText = async (response: Response) => {
|
|
try {
|
|
return (await response.text()).trim()
|
|
} catch {
|
|
return ''
|
|
}
|
|
}
|