feat(admin): add authenticated fetch helper
This commit is contained in:
parent
4d7ddf81ee
commit
22e53a7d30
1 changed files with 83 additions and 0 deletions
83
src/lib/server/admin/authenticated-fetch.ts
Normal file
83
src/lib/server/admin/authenticated-fetch.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { error, redirect } from '@sveltejs/kit'
|
||||||
|
import type { RequestEvent } from '@sveltejs/kit'
|
||||||
|
import { getSessionUser } from '$lib/server/admin/session'
|
||||||
|
|
||||||
|
type FetchInput = Parameters<typeof fetch>[0]
|
||||||
|
|
||||||
|
export type AdminFetchOptions = RequestInit
|
||||||
|
|
||||||
|
export interface AdminFetchJsonOptions extends AdminFetchOptions {
|
||||||
|
parse?: 'json' | 'text' | 'response'
|
||||||
|
}
|
||||||
|
|
||||||
|
function adminPassword(): string {
|
||||||
|
return process.env.ADMIN_PASSWORD ?? 'changeme'
|
||||||
|
}
|
||||||
|
|
||||||
|
function withAuthHeader(init: RequestInit = {}): RequestInit {
|
||||||
|
const headers = new Headers(init.headers ?? {})
|
||||||
|
if (!headers.has('Authorization')) {
|
||||||
|
const credentials = Buffer.from(`admin:${adminPassword()}`).toString('base64')
|
||||||
|
headers.set('Authorization', `Basic ${credentials}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...init,
|
||||||
|
headers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function adminFetch(
|
||||||
|
event: RequestEvent,
|
||||||
|
input: FetchInput,
|
||||||
|
options: AdminFetchOptions = {}
|
||||||
|
): Promise<Response> {
|
||||||
|
const user = getSessionUser(event.cookies)
|
||||||
|
if (!user) {
|
||||||
|
throw redirect(303, '/admin/login')
|
||||||
|
}
|
||||||
|
|
||||||
|
const init = withAuthHeader(options)
|
||||||
|
const response = await event.fetch(input, init)
|
||||||
|
|
||||||
|
if (response.status === 401) {
|
||||||
|
throw redirect(303, '/admin/login')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
let detail: string | undefined
|
||||||
|
try {
|
||||||
|
const json = await response.clone().json()
|
||||||
|
detail = typeof json === 'object' && json !== null && 'error' in json ? String(json.error) : undefined
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
detail = await response.clone().text()
|
||||||
|
} catch {
|
||||||
|
detail = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error(response.status, detail || 'Admin request failed')
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function adminFetchJson<T>(
|
||||||
|
event: RequestEvent,
|
||||||
|
input: FetchInput,
|
||||||
|
options: AdminFetchJsonOptions = {}
|
||||||
|
): Promise<T> {
|
||||||
|
const { parse = 'json', ...fetchOptions } = options
|
||||||
|
const response = await adminFetch(event, input, fetchOptions)
|
||||||
|
|
||||||
|
if (parse === 'text') {
|
||||||
|
return (await response.text()) as unknown as T
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parse === 'response') {
|
||||||
|
return response as unknown as T
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json() as Promise<T>
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue