Files
MincedPad/frontend/src/api/client.ts
T
2026-07-14 13:01:21 +08:00

143 lines
3.5 KiB
TypeScript

export type TTLOption = '1h' | '24h' | '7d' | 'never'
export interface Item {
slug: string
kind: string
title: string
body?: string | null
file_name?: string | null
mime?: string | null
size_bytes?: number | null
is_public: boolean
burn_after_read: boolean
expires_at: string | null
created_at: string
view_count: number
id?: number
created_ip?: string
burned?: boolean
}
export interface WallResponse {
items: Item[]
page: number
page_size: number
total: number
}
export interface Ban {
ip: string
reason: string
created_at: string
}
async function parseError(res: Response): Promise<string> {
try {
const data = await res.json()
if (typeof data?.detail === 'string') return data.detail
return JSON.stringify(data?.detail ?? data)
} catch {
return res.statusText || `HTTP ${res.status}`
}
}
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(path, init)
if (!res.ok) {
throw new Error(await parseError(res))
}
if (res.status === 204) {
return undefined as T
}
return (await res.json()) as T
}
export function createItem(payload: {
body: string
title?: string
is_public: boolean
burn_after_read: boolean
ttl: TTLOption
}): Promise<Item> {
return request<Item>('/api/items', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
export function uploadItem(form: FormData): Promise<Item> {
return request<Item>('/api/items/upload', {
method: 'POST',
body: form,
})
}
export function fetchWall(page = 1, pageSize = 20): Promise<WallResponse> {
const q = new URLSearchParams({
page: String(page),
page_size: String(pageSize),
})
return request<WallResponse>(`/api/items/wall?${q}`)
}
export function fetchItem(slug: string): Promise<Item> {
return request<Item>(`/api/items/${encodeURIComponent(slug)}`)
}
export function itemFileUrl(slug: string): string {
return `/api/items/${encodeURIComponent(slug)}/file`
}
export function adminLogin(password: string): Promise<{ token: string }> {
return request<{ token: string }>('/api/admin/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password }),
})
}
function authHeaders(token: string): HeadersInit {
return { Authorization: `Bearer ${token}` }
}
export function adminListItems(token: string): Promise<{ items: Item[] }> {
return request<{ items: Item[] }>('/api/admin/items', {
headers: authHeaders(token),
})
}
export function adminDeleteItem(token: string, itemId: number): Promise<{ ok: boolean }> {
return request<{ ok: boolean }>(`/api/admin/items/${itemId}`, {
method: 'DELETE',
headers: authHeaders(token),
})
}
export function adminListBans(token: string): Promise<{ bans: Ban[] }> {
return request<{ bans: Ban[] }>('/api/admin/bans', {
headers: authHeaders(token),
})
}
export function adminCreateBan(
token: string,
payload: { ip: string; reason?: string },
): Promise<{ ok: boolean }> {
return request<{ ok: boolean }>('/api/admin/bans', {
method: 'POST',
headers: {
...authHeaders(token),
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})
}
export function adminDeleteBan(token: string, ip: string): Promise<{ ok: boolean }> {
return request<{ ok: boolean }>(`/api/admin/bans/${encodeURIComponent(ip)}`, {
method: 'DELETE',
headers: authHeaders(token),
})
}