103 lines
2.2 KiB
TypeScript
103 lines
2.2 KiB
TypeScript
type AuthUser = {
|
|
id: string
|
|
email: string
|
|
name: string
|
|
role?: string
|
|
type?: string
|
|
}
|
|
|
|
const getApiBase = () => {
|
|
if (typeof window !== 'undefined') {
|
|
return ''
|
|
}
|
|
|
|
const viteEnv = (import.meta as ImportMeta & {
|
|
env?: Record<string, string | undefined>
|
|
}).env
|
|
|
|
return viteEnv?.VITE_API_BASE_URL || process.env.VITE_API_BASE_URL || process.env.API_URL || ''
|
|
}
|
|
|
|
const getErrorMessage = async (response: Response) => {
|
|
const text = await response.text()
|
|
|
|
if (!text) {
|
|
return `Request failed with status ${response.status}`
|
|
}
|
|
|
|
try {
|
|
const data = JSON.parse(text) as { message?: string; error?: string }
|
|
return data.message || data.error || text
|
|
} catch {
|
|
return text
|
|
}
|
|
}
|
|
|
|
const authFetch = async <T>(path: string, init?: RequestInit): Promise<T> => {
|
|
const headers = new Headers(init?.headers)
|
|
const hasBody = init?.body !== undefined && init?.body !== null
|
|
|
|
if (hasBody && !headers.has('content-type')) {
|
|
headers.set('content-type', 'application/json')
|
|
}
|
|
|
|
const response = await fetch(`${getApiBase()}${path}`, {
|
|
...init,
|
|
credentials: 'include',
|
|
headers,
|
|
})
|
|
|
|
if (!response.ok) {
|
|
throw new Error(await getErrorMessage(response))
|
|
}
|
|
|
|
if (response.status === 204) {
|
|
return undefined as T
|
|
}
|
|
|
|
return await response.json() as T
|
|
}
|
|
|
|
export const signInWithPassword = async (email: string, password: string) => {
|
|
return await authFetch<{
|
|
redirect: boolean
|
|
token: string
|
|
url?: string
|
|
user: AuthUser
|
|
}>('/api/auth/sign-in/email', {
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
email,
|
|
password,
|
|
}),
|
|
})
|
|
}
|
|
|
|
export const requestMagicLink = async (email: string) => {
|
|
return await authFetch<{ status: boolean }>('/api/auth/sign-in/magic-link', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ email }),
|
|
})
|
|
}
|
|
|
|
export const verifyMagicLink = async (token: string) => {
|
|
return await authFetch<{
|
|
token: string
|
|
user: AuthUser
|
|
session: {
|
|
id: string
|
|
userId: string
|
|
expiresAt: string
|
|
}
|
|
}>(`/api/auth/magic-link/verify?token=${encodeURIComponent(token)}`, {
|
|
method: 'GET',
|
|
})
|
|
}
|
|
|
|
export const signOut = async () => {
|
|
return await authFetch('/api/auth/sign-out', {
|
|
method: 'POST',
|
|
body: JSON.stringify({}),
|
|
})
|
|
}
|